Missing > character for keyboard reassignment

Microsoft PowerToys version
0.60.1
Running as admin

[X] Yes

Area(s) with issue?
Keyboard Manager
Steps to reproduce
I was trying to do a simple job. My new Notebook was supplied with a US keyboard and I am used to using a German one. They provided stickers to relabel the keys. Unfortunately, German keyboards have one additional key, not provided on US keyboards.
It is located between the left shift key and Y (on US keyboard, Z) and is for the following keys
Normal: <
Shift: >
ALT-GR: ¦
The first two are used a lot in programming, so I was trying to use PowerToys to assign as follows:'
ALT-Y: <
ALT-X: >
ALT-C: ¦
When trying to program this, I need to assign the > character from the list of characters. While I can find  < in the mapped to list, both > and ¦ are missing and therefore cannot be assigned.
How can I fix this?
Another problem I have is that although I have Win 11 installed in English, just the keyboard is defined in German, PowerToys is displayed in German and I have not found a setting to manually select the language.
✔ Expected Behavior
During keyboard reassignment, to be able to assign ALT-X: >
ALT-C: ¦
❌ Actual Behavior
Those charchters were not listed and as my keyboard is missing that button, I could not type it.
Other Software
No response

What keys do you normally press to get those characters?

I have not found a setting to manually select the language.

Not sure, but I'm guessing this needs to be in order first. Have a look at Windows Settings.

Thanks for your responses. I managed to fix the language issue in the Windows region settings. The attached image shows a classic German keyboard and the KUU G5 after the German stickers were applied (marked in yellow). There was no sticker provided for the #-key.
In the end, I decided to reprogram the Apps/Menu key as a replacement for the missing "<>|" key, as shown on the picture in red, as the Apps/Menu key is very rarely needed.
Using Powerkey's Keyboard Manager, I used "Keys" to replace "Apps/Menu" with "<", selected from the list.
Then I used Remap Shortcuts to:
Map "Shift, Apps/Menu" to "^´" (this represents ">" and the reason for my confusion).
I found that Right-Alt (on German keyboards labled Alt Gr) + Apps/Menu was already mapped to show "|"
I tried to map Left-Alt + Apps/Menu to the original Apps/Menu command, but that failed to work.

You can use AutoHotkey to assign special characters to Keyboards that don't have them

/dup https://github.com/microsoft/PowerToys/issues/25739


Probably typo in "Analyzing JS react native"

Looks like some verb is missing

In the next release


Overwrite of element attributes with default value

What problem does this feature solve?
For custom HTML elements Vue overwrites some attributes with default values. One example is the selected attribute which will always be set to selected=selected for truthy values. This absolutely makes sense for HTML elements that are defined in the HTML standard, but not for custom elements. Custom elements are not yet a web standard but there are already ways to build and use them. We experienced the issue when experimenting with a custom element for a tab bar which has the selected tab stored in a selected attribute.
Find a code example in:
https://jsfiddle.net/1pc2e9yj/2/
(The code example is currently limited to Chrome and Safari, because of missing polyfills for other browsers)
What does the proposed API look like?
Vue already has a config setting for ignoredElements. My proposal would be, that attributes are not set to their default values for elements in the ignoredElements array. If that approach is valid, then the example code in the jsfiddle would stay the same, but the output would change to "selected attribute value: tab1"

I have fixed this in #6885.


add arb owner

Line 739 is where the change actually is.
Tldr, we are going to use whitelist operator 1 as our default operator. Essentially all orderbooks will be added to this operator + any other smart contracts we want to add (maybe a lending protocol and stuff).
What I added: Allow the factory OR chain owner to add contracts to default ID 1

oops i didnt mean to PR this into main, was supposed to just be for me lmao


Default config don't creates on Ubuntu 16.04

$ transmission-cli tl
[critical] Config file not found: /home/popstas/.transmission-cli.yml

But creates on macOS.
Related: #35

I noticed this too on Ubuntu. I copied the transmission-cli.dist.yml over and edited it.

Was closed in #35, but unreleased. Now it fixed in v0.7.0 release.


SUBMARINE-389. [WEB]Implement the home page in workbench with Angular

What is this PR for?

Implement the home page in the workbench with Angular.
The style configuration "workbenchContent" caused impacts on the whole workbench, but the configuration is only for the workspace component. In addition, the frontend of the Manager/User page is influenced. Due to this reason, the style configuration needs to be separated from the whole workbench to the workspace component only.

What type of PR is it?
[Feature]
Todos

[ ] - Task

What is the Jira issue?
https://issues.apache.org/jira/browse/SUBMARINE-389
How should this be tested?
https://travis-ci.org/kevin85421/hadoop-submarine?utm_medium=notification&utm_source=github_status
Screenshots (if appropriate)
case1: >= 1600px

case2: < 1600px

workspace page

Manager/User page

Questions:

Does the licenses files need an update? No
Is there breaking changes for older versions? No
Does this needs documentation? No

@jasoonn @liuxunorg Can you help me review the pull request? Thanks!

@liuxunorg Thank you for your review!
@jasoonn I have updated the pull request, and some changes are related to the workspace component. Can you help me review the new changes? Thanks a lot!

@kevin85421 Thank you for contribution.
They are great!

Will merge if no more comments.


Convert Safetensors to an Ollama model

This (admittedly very large) change converts a safetensors file in the FROM line of a Modelfile into a 16 bit non-quantized Ollama model without having to use llamacpp's convert.py script. This initial version works with Mistral v0.2 (and presumably v0.1 although I haven't yet tested this), and with some tweaks should probably also work with Gemma as well.
Some things to note:

Currently it only works with SentencePiece tokenization. We can add BPE and other Tokenizers in the future to support more model types
Quantization is not yet supported, but this will be supported in a future change to easily allow different quantization levels
LORA adapters don't currently work, but it would be nice to support his in the future
llamacpp requires repacking the q and k attention layers to swap some of the axes. This required pulling in the gorgonia.org/tensors package which is somewhat abandoned. I forked that library and it's hosted at github.com/pdevine/tensors but ideally we wouldn't have to fork it.
A lot of processing is around converting brainfloat16 numbers into float16.
I mapped all of the params that Mistral required to build the GGUF file, but there are probably some that are missing. Those should be added as we support more models.
I haven't yet added unit tests here, which would be really nice, but there's so much to test!

Just adding this comment for reference.
Here is my modelfile for mistral-7b conversion:
FROM "/Users/bruce/models/Mistral-7B-Instruct-v0.2"
TEMPLATE """[INST] {{ .System }} {{ .Prompt }} [/INST]"""
PARAMETER num_ctx 2048
PARAMETER stop "</s>"
PARAMETER stop "[INST]"
PARAMETER stop "[/INST]"
PARAMETER stop "<<SYS>>"
PARAMETER stop "<</SYS>>"

Anything we can do to detect and fail earlier when trying to convert unsupported models?

This fail to convert mistral based finetunes, which surprises me. Something might be slightly off.
Steps to reproduce:

Download a Mistral-7b based finetune. (ex: https://huggingface.co/cognitivecomputations/dolphin-2.6-mistral-7b-dpo-laser)
Create the modelfile and reference the safetensors directory:

FROM "/Users/bruce/models/dolphin-2.6-mistral-7b-dpo-laser"
TEMPLATE """[INST] {{ .System }} {{ .Prompt }} [/INST]"""
PARAMETER num_ctx 2048
PARAMETER stop "</s>"
PARAMETER stop "[INST]"
PARAMETER stop "[/INST]"
PARAMETER stop "<<SYS>>"
PARAMETER stop "<</SYS>>"

Create the model: ./ollama create test/mistral-convert -f ~/models/test-convert-mistral/Modelfile
Run the model: ./ollama run test/mistral-convert

client error:
$ ./ollama run test/mistral-convert
Error: error loading model /Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7

server error:
loading library /var/folders/w3/_xhs6gvj6611sw6r36t7zp0m0000gn/T/ollama2833411254/metal/libext_server.dylib
time=2024-03-04T15:38:03.188-05:00 level=INFO source=dyn_ext_server.go:90 msg="Loading Dynamic llm server: /var/folders/w3/_xhs6gvj6611sw6r36t7zp0m0000gn/T/ollama2833411254/metal/libext_server.dylib"
time=2024-03-04T15:38:03.188-05:00 level=INFO source=dyn_ext_server.go:150 msg="Initializing llama server"
[1709584683] system info: AVX = 0 | AVX_VNNI = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | MATMUL_INT8 = 0 |
llama_model_loader: loaded meta data with 21 key-value pairs and 291 tensors from /Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7 (version GGUF V3 (latest))
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
llama_model_loader: - kv   0:                       general.architecture str              = llama
llama_model_loader: - kv   1:                               general.name str              = test/mistral-convert
llama_model_loader: - kv   2:                       llama.context_length u32              = 32768
llama_model_loader: - kv   3:                     llama.embedding_length u32              = 4096
llama_model_loader: - kv   4:                          llama.block_count u32              = 32
llama_model_loader: - kv   5:                  llama.feed_forward_length u32              = 14336
llama_model_loader: - kv   6:                 llama.rope.dimension_count u32              = 128
llama_model_loader: - kv   7:                 llama.attention.head_count u32              = 32
llama_model_loader: - kv   8:              llama.attention.head_count_kv u32              = 8
llama_model_loader: - kv   9:     llama.attention.layer_norm_rms_epsilon f32              = 0.000010
llama_model_loader: - kv  10:                       llama.rope.freq_base f32              = 10000.000000
llama_model_loader: - kv  11:                          general.file_type u32              = 1
llama_model_loader: - kv  12:                       tokenizer.ggml.model str              = llama
llama_model_loader: - kv  13:                      tokenizer.ggml.tokens arr[str,32000]   = ["<unk>", "<s>", "</s>", "<0x00>", "<...
llama_model_loader: - kv  14:                      tokenizer.ggml.scores arr[f32,32000]   = [0.000000, 0.000000, 0.000000, 0.0000...
llama_model_loader: - kv  15:                  tokenizer.ggml.token_type arr[i32,32000]   = [2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...
llama_model_loader: - kv  16:                tokenizer.ggml.bos_token_id u32              = 1
llama_model_loader: - kv  17:                tokenizer.ggml.eos_token_id u32              = 2
llama_model_loader: - kv  18:            tokenizer.ggml.unknown_token_id u32              = 0
llama_model_loader: - kv  19:               tokenizer.ggml.add_bos_token bool             = true
llama_model_loader: - kv  20:               tokenizer.ggml.add_eos_token bool             = false
llama_model_loader: - type  f32:   65 tensors
llama_model_loader: - type  f16:  226 tensors
llm_load_vocab: special tokens definition check successful ( 259/32000 ).
llm_load_print_meta: format           = GGUF V3 (latest)
llm_load_print_meta: arch             = llama
llm_load_print_meta: vocab type       = SPM
llm_load_print_meta: n_vocab          = 32000
llm_load_print_meta: n_merges         = 0
llm_load_print_meta: n_ctx_train      = 32768
llm_load_print_meta: n_embd           = 4096
llm_load_print_meta: n_head           = 32
llm_load_print_meta: n_head_kv        = 8
llm_load_print_meta: n_layer          = 32
llm_load_print_meta: n_rot            = 128
llm_load_print_meta: n_embd_head_k    = 128
llm_load_print_meta: n_embd_head_v    = 128
llm_load_print_meta: n_gqa            = 4
llm_load_print_meta: n_embd_k_gqa     = 1024
llm_load_print_meta: n_embd_v_gqa     = 1024
llm_load_print_meta: f_norm_eps       = 0.0e+00
llm_load_print_meta: f_norm_rms_eps   = 1.0e-05
llm_load_print_meta: f_clamp_kqv      = 0.0e+00
llm_load_print_meta: f_max_alibi_bias = 0.0e+00
llm_load_print_meta: n_ff             = 14336
llm_load_print_meta: n_expert         = 0
llm_load_print_meta: n_expert_used    = 0
llm_load_print_meta: pooling type     = 0
llm_load_print_meta: rope type        = 0
llm_load_print_meta: rope scaling     = linear
llm_load_print_meta: freq_base_train  = 10000.0
llm_load_print_meta: freq_scale_train = 1
llm_load_print_meta: n_yarn_orig_ctx  = 32768
llm_load_print_meta: rope_finetuned   = unknown
llm_load_print_meta: model type       = 7B
llm_load_print_meta: model ftype      = F16
llm_load_print_meta: model params     = 7.24 B
llm_load_print_meta: model size       = 13.49 GiB (16.00 BPW)
llm_load_print_meta: general.name     = test/mistral-convert
llm_load_print_meta: BOS token        = 1 '<s>'
llm_load_print_meta: EOS token        = 2 '</s>'
llm_load_print_meta: UNK token        = 0 '<unk>'
llm_load_print_meta: LF token         = 13 '<0x0A>'
llm_load_tensors: ggml ctx size =    0.22 MiB
llama_model_load: error loading model: create_tensor: tensor 'token_embd.weight' has wrong shape; expected  4096, 32000, got  4096, 32001,     1,     1
llama_load_model_from_file: failed to load model
llama_init_from_gpt_params: error: failed to load model '/Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7'
{"function":"load_model","level":"ERR","line":456,"model":"/Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7","msg":"unable to load model","tid":"0x176e3b000","timestamp":1709584683}
time=2024-03-04T15:38:03.216-05:00 level=DEBUG source=dyn_ext_server.go:157 msg="failure during initialization: error loading model /Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7"
time=2024-03-04T15:38:03.216-05:00 level=WARN source=llm.go:162 msg="Failed to load dynamic library /var/folders/w3/_xhs6gvj6611sw6r36t7zp0m0000gn/T/ollama2833411254/metal/libext_server.dylib  error loading model /Users/bruce/.ollama/models/blobs/sha256:5cb086c1722a8ef3fa169a0ae4406f3916f6ea315581db270dc59ed48c0c1cb7"

The Mistral finetunes should work now. It ended up being a problem with the vocab being the wrong size (in the case of dolphin-mistral it was missing the 32,001th token). There still is a bit of weirdness with dolphin-mistral in that the vocab uses </s> for the stop token, but I think eric is trying to remap that to <|im_stop|>. The convert.py script actually gets this wrong as well.


An RFC for structured logging

For #149
Includes #281 as an alternative design

Rendered
Reference implementation
Sample API docs

Hi! So we've been working through some designs in #149 on how structured logging could be supported in log. I've since gone away and tried to capture the results of that discussion and my experiments over the year into a concrete proposal that we can refine further. It looks pretty long, but a fair chunk of that is blocks of source.
I don't necessarily expect us to merge this PR even once we're happy with it, it's really just an effort to get more directed feedback on a significant API using a process we're familiar with in the Rust community.
cc @sfackler @dpc (who I know has limited capacity) @Thomasdezeeuw

Open questions:

[x] macro syntax

As for serde, I want to raise an issue of serde not being capable of serializing empty arrays correctly, i e, the type information gets lost. As this issue was closed as "wontfix", this made serde impossible to use for some formats (such as mine).
Maybe that limitation is not severe enough for this case - that's up to the rest of you to decide - but I think it's something to be aware of when choosing a serialization crate.

@diwic while I typically love using serde, there were multiple times where I was not happy with it trying to implement the backend (some serialization logic etc). It was typically related to handling sequences. Each time I dug, there were good reasons for how things were the certain way. So I can relate, but it is a de facto standard, at least right now.
It seems to me, having serde dependency behind a feature flag, and thus the ability to support other solutions (or serde versions) is a good thing to have.

I'm not convinced that a serde dependency is the right choice for this use case. The log crate needs to focus on being lightweight in terms of code generation in particular. I think a virtual-dispatch based approach may work out better there.

For some prior art, over in Haskell-land I've been using katip. Not perfect (perhaps particularly on the serialization side?), but it's worked better than previous libraries I'd used that attempted to provide single input / multi output structured logging w/ support for ordinary text logs.
A faculty for flipping between nginx-style / apache compatible / etc. text logging would be neat too.

I haven't wrapped my head around all the details yet, but requiring serde as a depending feels kind of heavy handed to me (especially since, as far as I can tell, there is no way for the top-level crate to disable the serde integration).
I would personally feel better about this if there was another feature, only for use in the top-level crate, that would act as a "kill switch", removing the dependency on serde. (I'm not sure if that is feasible. For the log-recording part it should be, I think; for the sinks I'm not so sure).

It sounds like we're not sold on depending on serde here. There certainly are some big complications. The rationale was that if we don't use serde::Serialize and serde::Serializer then we'll need to define traits that are conceptually very similar to them. We'll either have to comprimise in what can be represented by punting on the advanced APIs that make Serializer complex, like sequences and maps, or we'll have to introduce another API with extra cognitive load for libraries like uuid to implement.
So maybe we can find an API that doesn't bake in serde, and can be naturally object-safe, but also doesn't require libraries to opt-in to supporting structured logging as well as generic serialization. What I'd really like to avoid is accumulating a random bag of arbitrary traits in the ecosystem over time that need to be implemented before new libraries can be considered useful.

I think that adding a dependency to include a trait is not a big issue. The problem is that in the case of serde, this dependency is huge, which adds a non-negligible cost to everybody using log even if they are not using structured logging.
If instead of requiring full serde, we would just require a thin serde-traits crate instead, then the cost of pulling the trait might be insignificant, and just adding it by default a non issue.

So, the easiest way is just to say that keys are &str and values are Display. (Maybe there could be something that outputs the Debug string rather than the Display string.)
Maybe we could just start there?
Because the moment we cross that line and say that we want something else than strings in the log backend, may it be integers, arrays, maps, booleans or something else, the question becomes much more complex because  a) we then have to decide what we want to support and what we want to not support and b) all log backends need to implement support for this as well.
Surely serde can help with both, but then we're stuck with serde's design choices. Are we happy with that?

One question that isn't clearly answered from reading the RFC:
Can metadata be interpolated into the log message itself? We used a home-built structured logging framework for python and have generally captured all arguments as metadata, even if they showed up in the message. That way we don't have to write arguments twice (once as metadata, once as logging arguments) Given that all metadata must support Display in some form this seems trivial to support?

@matthieu-m

Is it possible/desirable to have a context argument?

You logging backend could implement it and be initialized with desired metadata, that would add to key-value pairs when serializing them.
If you want explicit context, that you could manipulate and pass at runtime, like in slog, then it seems it's not part of this RFC, and the general opinion is that "contextual logging" is a bit intrusive and it shouldn't be part of log itself. It could however still be provided by libraries like slog, and by having structured logging in log the interoperability between them would be better.

So, the easiest way is just to say that keys are &str and values are Display. (Maybe there could be something that outputs the Debug string rather than the Display string.)
Maybe we could just start there?

I like the sentiment here, but I don't think we can really start with Display and build up, because Display is simply not sufficient for structured logging. All you get are strings, which is really limiting for log servers that use that structure for querying later.

The examples show key-value pairs being passed explicitly, however I could imagine wanting to prepare a "map" of key-value pairs ahead of time (a context) and passing that to multiple logs.

Sure can! While preparing this RFC I put together an an experimental repo for this kind of contextual logging.

Can metadata be interpolated into the log message itself?

This is something we do in .NET using message templates. I think an alternative implementation of the log! macro, implemented using procedural macros, could do this. The syntax for the log! macro proposed here is pretty sparten to remain compatible and to encourage crates to experiment with their own log! macros. Interpolating from a single bag of data is one thing to try. Another is being able to log an impl Fail or impl Error as an argument directly in error!.

My sticking points on the serialization question are:

Don't require libraries depend on log and implement a trait before their types can be logged
Don't limit the kinds of data structures thst can be logged to anything less than serde supports

We possibly can find a way to achieve that without baking in serde directly, but creating a pervasive alternative serialization framework to support just for logging, or arbitrarily limiting what can be logged both seem like non-starters to me.

The log crate needs to focus on being lightweight in terms of code generation in particular. I think a virtual-dispatch based approach may work out better there.

@sfackler this is using virtual dispatch by way of erased-serde, but that crate has to work fairly hard to make that possible. There aren't any generics involved outside of trait implementations, which would probably exist in any implementation.
The lightweight code generation requirement is an interesting one I haven't really thought about at all. Could you elaborate on it a bit more? Which usergroup is it important to?

I believe the author of serde may also have some ideas for a lighter weight serde alternative
https://github.com/dtolnay/miniserde/blob/master/README.md

Looking at the complexity concern with serde (just ignoring the compile-time / artifact-size concerns for now), we could make life easier for formats that aren't already in the context of serialization (like env_logger's human-centric format) by tweaking the required trait bounds for logging. Instead of picking either Display or Serialize for structured values and facading that decision behind another implementation of Serialize, we could always require both Debug and Serialize, letting consumers pick the one they want. So if you're already writing logs using write! where structure is lost already you can keep doing that, and if you want to serialize the whole lot as a structure-retaining fornat like json you can do that too.

I like the sentiment here, but I don't think we can really start with Display and build up, because Display is simply not sufficient for structured logging. All you get are strings, which is really limiting for log servers that use that structure for querying later.

Strings are indeed all we get with such a proposal, which makes writing a backend to "log" a lot easier, than if it has to support all data types that someone somewhere has decided.
If there are technical difficulties with starting with "anything Display" as values, how about starting with only &str? That shouldn't cause any concerns with backwards compatibility later.

This seems like a proposal for a white horse with black stripes that we are told ought not be considered a zebra.  I hope that I'm wrong but signs are showing that the stdlibbers are hungry to grow stdlib beyond its original mandate.
I'm not an advocate of stdlib crates competing with those of the rest of the ecoystem.  stdlib Rust should foster cooperation, not competition.  So, what would I need slog for if its main features were made available in a stdlib log crate?  This is a question I and others will have if core functional overlaps exist.
I'm very interested in seeing a proposal for structured logging that is novel and does not overlap.  Why would the authors of slog want to refactor working code just to make it line with stdlib team designs?  Why should they?

@Dowwie https://github.com/KodrAus/log/blob/rfc/structured-logging/rfcs/0000-structured-logging.md#why-do-we-need-structured-logging-in-log

Strings are indeed all we get with such a proposal, which makes writing a backend to "log" a lot easier, than if it has to support all data types that someone somewhere has decided.

I totally agree with you there. Having to write a Serializer just to write values for a human-centric format is a formidable barrier-to-entry. The Using Visitor to print or serialize key value pairs section has some examples of how human-centric and machine-centric formats could support structured logging. The human-centric one is actually a bit intellectually dishonest though because it omits the few hundred lines of Serializer code for brevity. However, if we make all values Debug + Serialize instead of just Serialize, then a human-centric log format using std::fmt could be as simple as:
fn write_pretty(w: impl Write, r: &Record) -> io::Result<()> {
    // Write the first line of the log record
    ...

    // Write each key-value pair using the `WriteKeyValues` visitor
    record
        .key_values()
        .visit(&mut WriteKeyValues(w))
        .map_err(|e| io::Error::new(io::ErrorKind::Other, e.into_error()))
}

struct WriteKeyValues<W>(W);

impl<'kvs, W> Visitor<'kvs> for WriteKeyValues<W>
where
    W: Write,
{
    fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> {
        // Write the key and value pair
        writeln!(&mut self.0, "{}: {:?}", k, v)
    }
}

For properly structured formats, where a Serializer implementation already exists, like serde_json, the structured logging support can still be standard serde code:
fn write_json(w: impl Write, r: &Record) -> io::Result<()> {
    let r = SerializeRecord {
        lvl: r.level(),
        ts: epoch_millis(),
        msg: r.args().to_string(),
        props: r.key_values().serialize_as_map(),
    };

    serde_json::to_writer(w, &r)
}

#[derive(Serialize)]
struct SerializeRecord<KVS> {
    lvl: Level,
    ts: u64,
    msg: String,
    #[serde(flatten)]
    props: KVS,
}

So I think the Debug + Serialize approach could be a way to let implementors of Log use either the std::fmt machinery, or the serde::Serializer machinery as appropriate, without sacrificing structure altogether like we would with &str.
I have also been hacking around with serde itself following @gnzlbg's suggestion of a thin serde-traits library, and without all the impl blocks and other machinery on my box I can get a fresh serde to compile in ~2 seconds instead of ~9 (log currently takes ~1 second), so maybe there's an avenue to explore there too.

@Dowwie
The problem with logging is that it's an interoperability problem. Almost every crate does some logging, and if parts of ecosystem use different logging, then in practice it will degrade to lowest common denominator.
Also, I don't know about others but I don't want to be maintaining libraries if I could get the same functionality from (semi-)stdlib.
@KodrAus

I totally agree with you there. Having to write a Serializer just to write values for a human-centric format is a formidable barrier-to-entry.

Most people just take a one that was written and doesn't care. I have used json, yaml, cbor and probably many other serialization formats in Rust, and I never had to implement own serde serializer.
Even less so with logging, where people typically just want to dump a bunch of json logs to ElasticSearch. For slog rarely (never?) do I hear about people wanting human-centric input other than what slog-term implements.  Anyone can have preferences, but it's just not worth implementing again for every project.

@KodrAus

Can metadata be interpolated into the log message itself?

This is something we do in .NET using message templates. I think an alternative implementation of the log! macro, implemented using procedural macros, could do this.

slog already does something like this, I think without proc macros; named format arguments are interpreted as structured metadata in addition to being formatted, while anonymous ones aren't. It's nice to use.

Ok, I spent some time exploring the serialization API more to remove the required serde dependency from this RFC and came up with a proof-of-concept.
This API doesn't require serde by default, but can support it using a Cargo feature. Without the serde feature enabled it's super lightweight, the idea being that out-of-the-box we just support a limited set of primitive types as structured key-value pairs, but that set can be expanded to any type that implements serde::Serialize when the feature is opted into. The Serialize type from this crate doesn't become a public dependency because you can't implement it manually (that also lets us change its default bounds using Cargo features without fear of breakage), and a reasonable set of types is still supported even without the serde feature.
I think this approach can mitigate some of the concerns about serde being too heavyweight. Any thoughts?

Ok, what do we think of this API, based on the serialization proof-of-concept I posted before? I've stuck with the visit terminology over the serialize terminology because actually serializing a key-value pair is just one thing you might want to do with them. Other things include checking whether a specific pair is present and doing something with its primitive type, like comparing the timestamp on a record to one that was previously set in some ambient context to calculate the elapsed time.
I changed KeyValueSource to Source, Visitor to SourceVisitor, and Serialize to Visit. The Error type is basically the same as what's currently in the RFC.
// --- Set of key-value pairs --- //

pub trait Source {
    fn visit<'kvs>(&'kvs self, visitor: &mut dyn SourceVisitor<'kvs>) -> Result<(), Error>;

    // --- Provided adapter methods --- //
    // Using these you probably don't to build a `SourceVisitor` yourself

    fn erase(&self) -> ErasedSource
    where
        Self: Sized,
    {}

    fn try_for_each<F, E>(self, f: F) -> Result<(), Error>
    where
        Self: Sized,
        F: FnMut(Key, Value) -> Result<(), E>,
        E: Into<Error>,
    {}

    fn get<'kvs, Q>(&'kvs self, key: Q) -> Option<Value<'kvs>>
    where
        Q: Borrow<str>,
    {}

    #[cfg(feature = "serde")]
    fn serialize_as_map(self) -> SerializeAsMap<Self>
    where
        Self: Sized,
    {}

    ...
}

pub trait SourceVisitor<'kvs> {
    fn visit_pair(&self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>;
}

// --- Individual key or value --- //

// Can't be implemented manually
// It's either a fixed set of `std` types or `T: Serialize`
pub trait Visit: private::Sealed {
    fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error>;
}

#[cfg(not(feature = "serde"))]
impl Visit for $std_ty {} // where $std_ty is `u8`, `bool`, `&str`, `&Path` etc

#[cfg(feature = "serde")]
impl<T: Serialize + ?Sized> Visit for T {} // covers all $std_ty impls + a bunch more

pub trait Visitor {
    fn visit(&mut self, v: &dyn Visit) -> Result<(), Error> {
        v.visit(self)
    }

    fn visit_i64(&mut self, v: i64) -> Result<(), Error>;
    fn visit_u64(&mut self, v: u64) -> Result<(), Error>;
    fn visit_f64(&mut self, v: f64) -> Result<(), Error>;
    fn visit_bool(&mut self, v: bool) -> Result<(), Error>;
    fn visit_char(&mut self, v: char) -> Result<(), Error>;
    fn visit_str(&mut self, v: &str) -> Result<(), Error>;
    fn visit_bytes(&mut self, v: &[u8]) -> Result<(), Error>;

    // If a format is given a complex `serde` datastructure, like a map or sequence
    // A format might want to write a placeholder value like `null` instead
    fn unsupported(&mut self) -> Result<(), Error> {
        Err(Error::msg("unsupported value"))
    }
}

// --- Concrete types for keys and values --- //

// `ToOwned::Owned: Send + Sync + 'static`?
// use `Borrow<str>` as the generic trait bound for keys
pub struct Key<'k> {}

impl<'k> Key<'k> {
    fn as_str(&self) -> &str {}
}

// `Key` is always `Visit`
// If `serde` is available, it's also `Serialize`
#[cfg(not(feature = "serde"))]
impl<'k> Visit for Key<'k> {}
#[cfg(feature = "serde")]
impl<'k> Serialize for Key<'k> {}

// `ToOwned::Owned: Send + Sync + 'static`?
// use `Visit` as the generic trait bound for values
pub struct Value<'v> {}

// `Value` is always `Visit`
// If `serde` is available, it's also `Serialize`
#[cfg(not(feature = "serde"))]
impl<'v> Visit for Value<'v> {}
#[cfg(feature = "serde")]
impl<'v> Serialize for Value<'v> {}

the log! macros would be able to accept idents as keys, and any expression that implements Visit as values.
Why Visitor and SourceVisitor?
The API could be smaller if there was just one Visitor type:
trait Visitor {
    fn visit_key(&mut self, v: Key);
    fn visit_value(&mut self, v: Value);
    fn visit_pair(&mut self, k: Key, v: Value) {}

    fn visit_i64(&mut self, v: i64);
}

This might seem like a simplification, but what it's really doing is just making the sequence of operations in serializing a key-value pair implicit. In our format that implements Visitor we'd need to think about values being serialized before keys, keys or values being serialized multiple times, or being given a primitive like visit_str instead of a key or value and having to guess which one it is.
So having one trait, SourceVisitor, that surfaces key-value pairs, and another trait, Visitor, that looks at individual values lets the format decide exactly how it's going to work with its pairs. It also means we can lean on SourceVisitor to work with pairs consistently so that ergonomic methods like try_for_each and serialize_as_map can be provided on Source. With the combination of these two methods I actually haven't found myself even needing to write a SourceVisitor manually in my experiments with this API.
Why structs for Key and Value instead of trait objects?
Using structures for Key and Value is for backwards compatibility. With a dyn Trait or impl Trait we've got all our cards on the table, and it's much harder to introduce changes without breakage. Using structures we don't have that problem. We could also abstract over owned and borrowed keys and values, and provide some useful methods using static rather than dynamic dispatch.

@KodrAus apologies for my ignorance, does log already support or will this structured logging change add support for multi-output from a single structured log record?

@bitemyapp This is unrelated to this feature. Technically it is possible with log already, but log would have to embrace "composable loggers", provide a common trait for interoperability to make this effortless and common (Like slogs Drain) Please open another issue (if one does not already exist). As far as I can tell this RFC is compatible with functionality like this already.

Ok, what do we think of this API, based on the serialization proof-of-concept I posted before?

I guess there will be some bikeshedding over exactly what types are to be expected here? E g, I don't see myself wanting to log a char very often (and if I do, I could log it as a str).
Another thought is what to do with Option. We could make that skip the key-value pair if the option is None and log the unwrapped contents if the option is Some, maybe?

For comparison: https://docs.rs/slog/2.4.1/slog/trait.Serializer.html

@bitemyapp Yeh, this is another feature that's a logical next step from structured logging, but I haven't tried to tackle it as part of this RFC besides trying not to design ourselves into a corner. Personally, I'm not sure that we need to support too much composition in log beyond what's needed on Record, because frameworks like slog already do this really well, and as a facade log doesn't necessarily need to care too much.

I guess there will be some bikeshedding over exactly what types are to be expected here? E g, I don't see myself wanting to log a char very often (and if I do, I could log it as a str).

@diwic Sure, let's colour some bikesheds!

We should add u128 and i128, I forgot about those.
Should we add methods for smaller numbers than 64-bit?
visit_char could default to visit_str, using a method like char::encode_utf8.
We should support Some and None in some way. Some could also default to just visit on its inner value.
Another type that might be worth treating explicitly is Instant.

What else do we think?
Other APIs for comparison
slog
pub trait Serializer {
    impl_default_as_fmt! {
        usize => emit_usize
    }
    impl_default_as_fmt! {
        isize => emit_isize
    }
    impl_default_as_fmt! {
        bool => emit_bool
    }
    impl_default_as_fmt! {
        char => emit_char
    }
    impl_default_as_fmt! {
        u8 => emit_u8
    }
    impl_default_as_fmt! {
        i8 => emit_i8
    }
    impl_default_as_fmt! {
        u16 => emit_u16
    }
    impl_default_as_fmt! {
        i16 => emit_i16
    }
    impl_default_as_fmt! {
        u32 => emit_u32
    }
    impl_default_as_fmt! {
        i32 => emit_i32
    }
    impl_default_as_fmt! {
        f32 => emit_f32
    }
    impl_default_as_fmt! {
        u64 => emit_u64
    }
    impl_default_as_fmt! {
        i64 => emit_i64
    }
    impl_default_as_fmt! {
        f64 => emit_f64
    }
    impl_default_as_fmt! {
        #[cfg(integer128)]
        u128 => emit_u128
    }
    impl_default_as_fmt! {
        #[cfg(integer128)]
        i128 => emit_i128
    }
    impl_default_as_fmt! {
        &str => emit_str
    }

    fn emit_unit(&mut self, key: Key) -> Result {
        self.emit_arguments(key, &format_args!("()"))
    }

    fn emit_none(&mut self, key: Key) -> Result {
        self.emit_arguments(key, &format_args!(""))
    }

    fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> Result;

    #[cfg(feature = "nested-values")]
    fn emit_serde(&mut self, key: Key, value: &SerdeValue) -> Result {
        value.serialize_fallback(key, &mut SerializerForward(self))
    }
}

Here we have default implementations for most of these methods that forward to emit_arguments. Based on these APIs, I can't see any issues with us adapting between log::Source and slog::KV.
serde
pub trait Serializer: Sized {
    type Ok;
    type Error: Error;

    type SerializeSeq: SerializeSeq<Ok = Self::Ok, Error = Self::Error>;
    type SerializeTuple: SerializeTuple<Ok = Self::Ok, Error = Self::Error>;
    type SerializeTupleStruct: SerializeTupleStruct<Ok = Self::Ok, Error = Self::Error>;
    type SerializeTupleVariant: SerializeTupleVariant<Ok = Self::Ok, Error = Self::Error>;
    type SerializeMap: SerializeMap<Ok = Self::Ok, Error = Self::Error>;
    type SerializeStruct: SerializeStruct<Ok = Self::Ok, Error = Self::Error>;
    type SerializeStructVariant: SerializeStructVariant<Ok = Self::Ok, Error = Self::Error>;

    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error>;

    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error>;
    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error>;
    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error>;
    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error>;
    serde_if_integer128! {
        fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
            let _ = v;
            Err(Error::custom("i128 is not supported"))
        }
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error>;
    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error>;
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error>;
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error>;
    serde_if_integer128! {
        fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
            let _ = v;
            Err(Error::custom("u128 is not supported"))
        }
    }

    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error>;
    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error>;

    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error>;
    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error>;
    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error>;

    fn serialize_none(self) -> Result<Self::Ok, Self::Error>;
    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize;

    fn serialize_unit(self) -> Result<Self::Ok, Self::Error>;
    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error>;
    fn serialize_unit_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
    ) -> Result<Self::Ok, Self::Error>;

    fn serialize_newtype_struct<T: ?Sized>(
        self,
        name: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize;
    fn serialize_newtype_variant<T: ?Sized>(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize;

    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error>;
    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error>;
    fn serialize_tuple_struct(
        self,
        name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleStruct, Self::Error>;
    fn serialize_tuple_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleVariant, Self::Error>;

    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error>;
    fn serialize_struct(
        self,
        name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error>;
    fn serialize_struct_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeStructVariant, Self::Error>;

    fn collect_seq<I>(self, iter: I) -> Result<Self::Ok, Self::Error>
    where
        I: IntoIterator,
        <I as IntoIterator>::Item: Serialize,
    {
        let iter = iter.into_iter();
        let mut serializer = try!(self.serialize_seq(iter.len_hint()));
        for item in iter {
            try!(serializer.serialize_element(&item));
        }
        serializer.end()
    }
    fn collect_map<K, V, I>(self, iter: I) -> Result<Self::Ok, Self::Error>
    where
        K: Serialize,
        V: Serialize,
        I: IntoIterator<Item = (K, V)>,
    {
        let iter = iter.into_iter();
        let mut serializer = try!(self.serialize_map(iter.len_hint()));
        for (key, value) in iter {
            try!(serializer.serialize_entry(&key, &value));
        }
        serializer.end()
    }
    #[cfg(any(feature = "std", feature = "alloc"))]
    fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Display,
    {
        use lib::fmt::Write;
        let mut string = String::new();
        write!(string, "{}", value).unwrap();
        self.serialize_str(&string)
    }
    #[cfg(not(any(feature = "std", feature = "alloc")))]
    fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Display;

    #[inline]
    fn is_human_readable(&self) -> bool {
        true
    }
}

Here we have a whole lot of extra complexity for just about any datastructure you can represent in Rust.

One major drawback of the design I posted is that while it doesn't technically make serde a required dependency, it does make it implicitly required for any implementation of Log, because there's a chance that some of the Values on a log record can't be captured by the Visitor trait (some library might opt in to serde and log a Vec or something). That's why there's that Visitor::unsupported method, but it's a cop-out and the experience of having to deal with it would be poor.
So I've been thinking about how slog::Serializer by default forwards its methods to emit_arguments. We could do something similar in log, so instead of getting handed some unrepresentable value in your Visitor, you can always at least write it using std::fmt. That would mean that instead of doing a blanket implementation of Visit for any serde::Serialize, we'd do a blanket implementation of Visit for any serde::Serialize + std::fmt::Debug.
So updating the API from before:
// --- Set of key-value pairs --- //

pub trait Source {
    fn visit<'kvs>(&'kvs self, visitor: &mut dyn SourceVisitor<'kvs>) -> Result<(), Error>;

    // --- Provided adapter methods --- //
    // Using these you probably don't to build a `SourceVisitor` yourself

    fn erase(&self) -> ErasedSource
    where
        Self: Sized,
    {}

    fn try_for_each<F, E>(self, f: F) -> Result<(), Error>
    where
        Self: Sized,
        F: FnMut(Key, Value) -> Result<(), E>,
        E: Into<Error>,
    {}

    fn get<'kvs, Q>(&'kvs self, key: Q) -> Option<Value<'kvs>>
    where
        Q: Borrow<str>,
    {}

    #[cfg(feature = "serde")]
    fn serialize_as_map(self) -> SerializeAsMap<Self>
    where
        Self: Sized,
    {}

    ...
}

pub trait SourceVisitor<'kvs> {
    fn visit_pair(&self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>;
}

// --- Individual key or value --- //

// Can't be implemented manually
// It's either a fixed set of `std` types or `T: Serialize`
pub trait Visit: private::Sealed {
    fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error>;
}

#[cfg(not(feature = "serde"))]
impl Visit for $std_ty {} // where $std_ty is `u8`, `bool`, `&str`, `&Path` etc

#[cfg(feature = "serde")]
impl<T: Serialize + Debug + ?Sized> Visit for T {} // covers all $std_ty impls + a bunch more

pub trait Visitor {
    fn visit(&mut self, v: &dyn Visit) -> Result<(), Error> {
        v.visit(self)
    }

    fn visit_i64(&mut self, v: i64) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    fn visit_u64(&mut self, v: u64) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }

    #[cfg(feature = "i128")]
    fn visit_u128(&mut self, v: u128) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    #[cfg(feature = "i128")]
    fn visit_i128(&mut self, v: i128) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }

    fn visit_f64(&mut self, v: f64) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    fn visit_bool(&mut self, v: bool) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    fn visit_char(&mut self, v: char) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    fn visit_str(&mut self, v: &str) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }
    fn visit_bytes(&mut self, v: &[u8]) -> Result<(), Error> {
        self.visit_args(format_args!("{:?}", v))
    }

    fn visit_none(&mut self) -> Result<(), Error> {
        self.visit_args(format_args!("None"))
    }

    fn visit_args(&mut self, args: &std::fmt::Arguments) -> Result<(), Error>;
}

// --- Concrete types for keys and values --- //

// `ToOwned::Owned: Send + Sync + 'static`?
// use `Borrow<str>` as the generic trait bound for keys
pub struct Key<'k> {}

impl<'k> Key<'k> {
    fn as_str(&self) -> &str {}
}

// `Key` is always `Visit`
// If `serde` is available, it's also `Serialize`
#[cfg(not(feature = "serde"))]
impl<'k> Visit for Key<'k> {}
#[cfg(feature = "serde")]
impl<'k> Serialize for Key<'k> {}

// `ToOwned::Owned: Send + Sync + 'static`?
// use `Visit` as the generic trait bound for values
pub struct Value<'v> {}

// `Value` is always `Visit`
// If `serde` is available, it's also `Serialize`
#[cfg(not(feature = "serde"))]
impl<'v> Visit for Value<'v> {}
#[cfg(feature = "serde")]
impl<'v> Serialize for Value<'v> {}
impl<'v> Debug for Value<'v> {}

That way a library implementing Log could always get a reasonable representation of any loggable value without depending on serde.

Alrighty, I've gone ahead and updated the RFC based on the feedback so far. It removes serde as a required dependency, so we can support structured logging without opting in to serde support. Instead, it makes serde an optional extensibility point in the ecosystem so that log doesn't become a massive public dependency of its own to support structured logging in a way that's nice for users to consume.
I've also added a section on how we could manage getting an owned version of the key-value pairs that is Send + Sync + 'static efficiently.
If you'd rather poke around an example implementation, I've got one here

@Thomasdezeeuw thanks for taking the time to read through this giant document! I appreciate your input on how readable it is(n't). Before working through those though maybe we should try address the main criticism you've raised.
When talking about this API being too big are you talking about the surface area or the volume? I mean, do you think the public API is too big, or do you think there's too much going on behind the scenes to support it?
Personally, I don't think the public API is too big here, but there's definitely a lot more going on than in the simplest API we could imagine for key-value pairs.
If we look at the simplest possible representation we can see where the API proposed here abstracts over it:
&[
    ("key1", 42),
    ("key2", &["a", "b", "c"]),
    ("key3": Uuid::new_v4()),
];

There are two abstractions that we work with:

Source
Visit

The Source trait abstracts storage for key-value pairs:
┌  &[
│     ("key1", 42),
│     ("key2", &["a", "b", "c"]),
│     ("key3": Uuid::new_v4()),
├ ];
└ `Source`

This abstraction is important for being able to convert records from other frameworks to and from records in log. If we only supported &[(&str, &Visit)] then we wouldn't be able to efficiently work with BTreeMap<String, Value>, or Arc<CustomMap>.
The Visit trait abstracts serialization for key-value pairs:
   &[
      ("key1", 42),
┌─────────────┴──┘
│     ("key2", &["a", "b", "c"]),
├─────────────┴────────────────┘
│     ("key3": Uuid::new_v4()),
├─────────────┴──────────────┘
│ ];
└ `Visit`

This abstraction is important (over just something like std::fmt::Debug) because we need to be able to inspect the structure of values in order to serialize them or maybe style them in a particular way in a terminal. We could just carry primitive values in an enum like serde_json::Value, but then we have the same problem we did with the concrete slice in Source. We make it more difficult for other logging frameworks to integrate with log when their values have a different underlying structure. Alternatively we could just carry a raw trait object and expect that to be downcast to a known concrete type. That wouldn't work either because log needs to support arbitrary log producers communicating with arbitrary log consumers. Unless you control both the producer and the consumer you couldn't reasonably know what to downcast that trait object to. So we need some serialization API that forces implementors to conform to some contract that lets anybody reasonably determine its structure.
The API proposed for Visit here is already a very simple serialization API. It's just one that can optionally carry serde::Serialize with it. That lets it punt a lot of complexity that would otherwise be introduced for arrays, maps, #[derive], formats, and managing breakage. We get all that for free(ish). There's internal (not public), module-local complexity to get these benefits, but I think localized complexity is much more manageable and is a fair tradeoff to make in log to offer the ecosystem the most appropriate API for producing and consuming structured logs.
So I appreciate the need to keep log tight and fundamental. I think based on the usecases they need to support, both Source and Visit do achieve a good balance. I've also got the assumption that structured logging is a really fundamental feature, so its API deserves an appropriate amount of complexity for the benefit that it brings the Rust ecosystem.
@sfackler do you have any more thoughts?

@KodrAus Saying that the crate would become too big wasn't clear, your right. What I meant was that the public API would become too big, i.e. it would have too many types (struct/traits). If I counted correctly 7 new types would be added to the public API. On top of that another couple helpers, e.g. SerializeAsMap, and more types when the serde feature is enabled, e.g. ErasedSource. The currently crate exposes 7 types (+ 2 builder types), which means the public API would be at least become twice as large.
For a "simple" logging facade it doesn't seem so simple to me.
But I do agree that structured logging is a fundamental feature. So perhaps with some structuring of the public API, e.g. by pushing most Log implemening types to the back, while pulling the logging macros to the front. This way "users" of the log crate keep a simple public API, while the "Log implementers" have to deal a more complex API.
P.S. I hope I wasn't too harsh with my feedback.

@KodrAus To continue the discussion around the Source and Visitor traits; could this be solved with an a serde_json::Value like type in an iterator, so &dyn Iterator<Item = (&str, Value)>. This means that BTreeMap<String, Value>, and similiar types, can be chained onto it. And Value will keep a type around. Of course Value can't possibly support all types, so maybe a variant should be Other(&dyn Display), or perhaps a similiar trait.
Upside is that the public API remains small, adds only a single type. Downside is that the order of the iterator is now unspecified, I'm unsure if this is important for you. And we still have fully typed value (only when they fit in Value) when logging.

For a "simple" logging facade it doesn't seem so simple to me.

Hmm, I wouldn't necessarily describe log as a simple logging facade. At least I wouldn't make simple its primary goal. Instead, I'd call log a fundamental logging facade. Simple is a nice goal, but fundamental is what would let log support the development of Rust's logging ecosystem rather than just being an obsolete artifact of it. Other people might have different opinions than me on that though.

could this be solved with an a serde_json::Value like type in an iterator

This is actually where I started earlier this year, but ran into some complications. The problem with an iterator approach is that it's not object safe, and making it object-safe requires some unappealing public machinery for progressing and resetting the iterator. So the Source and source::Visitor system is conceptually like IntoIterator and Iterator, but retains object safety by pushing the iteration inside the Source. You just get a chance to observe that iteration using a Visitor. This pattern lets us build a complimentary set of adapter methods like we already have with Iterator. Those adapters make it possible to normally work with a Source without having to define any Visitors at all. So the Source and source::Visitor approach results in the same number of traits to work with as we'd have with IntoIterator and Iterator.
The Value type you're describing isn't too far off the original version of Value from this RFC. I ended up changing it to Value, value::Visit and value::Visitor in the process of limiting serde's role in the API. It turns out it's roughly the same number of types. Even if you just have a concrete Value type you still need a trait like ToValue that can take an arbitrary T and turn it into a Value for the log! macros, and for records from other frameworks. Exposing the variants of that Value directly as an enum wouldn't be desirable because of backwards compatibility concerns. You'd either need a non-exhaustive catch-all arm when matching over the variants, or you wouldn't be able to add variants without breaking consumers. Neither of those options is appealing. So you'd end up needing a trait like value::Visitor anyways so callers could guarantee that all values can always be logged regardless of internal changes to the representation of the Value. So the Value, value::Visit and value::Visitor approach results in the same number of traits to work with as a concrete type does. I do feel like the value machinery deserves a lot of scrutiny though.
So I don't think just aiming to reduce the number of types is necessarily going to result in a more understandable public API. That lands us in a state where we make arbitrary limitations for the same of an API we assume is more understandable. We want the API to be understandable through its use. I think the way to do that is to look at the interactions of these types for unnecessary complications, rather than just treating their existence as a complication. It is definitely worth making each type justify its existence though.

To make it a little easier to explore, I've pushed up the API docs for this proposed API.

Ok, I've got something more to share on the serialization front. This library (with rough source here) is similar to the last one I posted, but has a smaller surface area. It's no-std, object-safe and integrates optionally with serde. It seems like something we could build structured logging serialization off.
For integrating an external serialization framework like this into log, we could either:

use the same approach the RFC proposes with serde. This doesn't offer any benefit over what the RFC currently proposes.
depend on it and use its API. That means log would have more than zero dependencies by default. This was a problem for serde because it takes a while to compile and object-safety requires boxing.
pull it into log so it doesn't directly consume an external serialization framework. That means implementors of this value type would need to depend on log.
provide our own simplified API and bridge them, using Cargo features to enable support. That means we'd make callers of the log! macros have to work a bit harder to serialize their structured values.

Personally, I'm thinking the third option could be worth pursuing, since callers would already not be able to log their serde datastructures by default.
Does anybody else have any thoughts?

I've managed to convince myself that it's not super important for structured logging in log to be able to capture all the types in the ecosystem that can be serialized by default. It's less user-friendly, but it leaves the door open for someone to build a better set of log! macros that can capture structured values differently, and could find their way back to log itself one day.
So if we don't need to bake a framework in out-of-the-box because we don't need to capture its values out-of-the-box, then we can probably move forward with a much slimmer values API, and see what direction tokio-trace and sval go in.
I was thinking a public API something like:
pub trait ToValue {
    fn to_value(&self) -> Value;
}

impl ToValue for u64 { .. }
impl ToValue for i64 { .. }
impl ToValue for str { .. }
// etc.

pub trait Visitor {
    // The only required method
    fn visit_any(&mut self, value: Value) -> Result<(), Error>;

    // Provided methods for primitives
    fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
        self.visit_any(value.to_value())
    }
}

pub struct Value<'a>(_);

impl<'a> Value<'a> {
    pub fn from_debug(v: &dyn fmt::Debug) -> Self { .. }

    pub fn visit(&self, visitor: impl Visitor) -> Result<(), Error> { .. }
}

impl<'a> fmt::Debug for Value<'a> { .. }

Internally, we can use private methods on Value to retain the structure of integers and other primitives when forwarding to a Visitor.
As a provider of dataustructures, like uuid, you can implement the ToValue trait yourself, but the only way to get a hold of a Value is to call ToValue on another type that already implements it, or use Value::from_debug.
As a consumer of structured logs like env_logger, if you don't care about structure, you can just implement Visitor::visit_any. If you do care, you can override provided methods like Visitor::visit_u64.
Later, to extend this system to support another serialization framework, like sval, we would:

Add a public Value::from_sval method or something.
Derive sval::Value for Value as well as fmt::Debug.

For serde it would look basically the same, but could be supported in terms of sval.
That way, any implementation for Visitor could treat a Value like a sval::Value or a fmt::Debug without anything else having to change.
That could be a way for us to unblock on the serialization question.

I think commas might be a better delimiter than semi-colons

Yeh, the proposed macro syntax is:
info!("format", unstructured, unstructured; structured, structured)

So there's a single semicolon that separates structured from unstructured fields, that are separated by commas. This is for backwards-compatibility, so that the macro can split the unstructured fields we already support today with trait bounds determined by the format string from the new structured ones that have different trait bounds. If we put them all together then we'd end up breaking existing uses.
Once we've landed some kind of structured logging though I'm keen to start experimenting with a new procedural macro based log! implementation externally that could peek into the format string and just interpolate from a single list of structured fields.

So there's a single semicolon that separates structured from unstructured fields, that are separated by commas. This is for backwards-compatibility, so that the macro can split the unstructured fields we already support today with trait bounds determined by the format string from the new structured ones that have different trait bounds. If we put them all together then we'd end up breaking existing uses.

Understood! I didn't realize that putting all of them together would be a breaking change. My bad for missing that.

A possible future extension we could make to Value would be letting you treat an error type explicitly:
impl<'v> Value<'v> {
    fn from_error(err: &'v impl std::error::Error) -> Self { .. }

    fn as_error(&self) -> Option<&dyn std::error::Error> { .. }
}

with whatever bounds it is we have to satisfy to make the returned error from as_error downcastable.

So we touched on this a bit at the all hands, and while I don't think any major red flags were raised, it is a vast piece of API to consider. At least we can land it incrementally.
One thing we didn't get a chance to look at is the macro syntax. Unlike the traits and structures we probably can't land a subset of the macro. Right now, the RFC macro syntax is:
log!(format, format_arg1, format_arg2, .. ; structured_arg1, structured_arg2);

It's a shame to have to repeat format args in the structured list if you don't just want them interpolated into the message, but I couldn't think of a backwards-compatible way to do it. But, maybe we could use a token in the macro to opt-in to treating format args as also structured:
log!(s; format, structured_arg1, structured_arg2, .. ; additional_arg1, additional_arg2);

So the leading s tells us format arguments should implement ToValue and be in the set of key-value pairs in addition to being interpolated into the format string, avoiding having to use two sets (which has been raised here a few times as not ideal), or having to break existing log! callers.
Does anybody have any other thoughts on their preferred macro syntax?

Using some kind of switch within the macro is a strategy that would also work for treating the output of #317 as an optionally structured value too.

Macro syntax: See alternative considered for #317, using '=' as marker token.  At least in that case it seemed to me, like more to teach with DSL, than is the case with dedicated function-like macros.  That said, the -v macros are more unique in that they are restricted to a single value.
Have or might you consider that as an alternative? Like a parallel set of -s or other suffix macros where all named parameters are consider structured and available for the message format as well?
Also, for many cases where this would actually be used, would it be adequate and/or potentially more convenient to just configure something with the logging implementation to format specific structured values in the output, either before or after the passed message?
(I've held off on commenting here, trying to catch up on its evolution and also understand the tokio stuff, but...)
Have or might you also consider adding the rust-log-mdc crate (and what I presume is its java log4j/slf4j inspiration) as prior art?  I think that is relevant because for use cases like including a user ID as structured attribute, it may be nice to "register" that value as a thread local or separately passed around "context" associate-array-like type. An exclusively thread-local approach doesn't work cleanly with async (which wasn't a Java thing, back in the day), at least without library support, but isn't tokio-trace's Span concept similar?

See alternative considered for #317, using '=' as marker token

The marker I was thinking of was something like kv, but = is a reasonable alternative too!

a parallel set of -s or other suffix macros

I think we could end up with a macro-explosion going the separate suite of macros route here, so something that doesn't require new macros for interpreting structured data differently would be better I think.

potentially more convenient to just configure something with the logging implementation to format specific structured values in the output

That's an interesting idea, and you could support it in a logging framework built on top of log's structured logging, but it would require the data we're interpolating directly into the message is also available as key-value pairs to process independently, which we need a marker token for in the existing macros.

Have or might you also consider adding the rust-log-mdc crate (and what I presume is its java log4j/slf4j inspiration) as prior art?

Thanks! I haven't looked at this crate before so will check it out.

I for one would much rather have separate macros that have good syntax than a single unified macro with awkward syntax. Couldn't they even have the same names, just residing in a different module and requiring manual import?

I for one would much rather have separate macros that have good syntax than a single unified macro with awkward syntax.

@Ralith do you find the info!(kv; "format", arg; arg) style awkward? If so, what is it you don't like?
There's a whole new world of improvements we could make to the log! macros if we built them as proc macros instead of using macro_rules v1 like they are today. That's something I want to explore in a separate crate before trying to circle back into log. Without proc macros I think the structured logging support we can ship is bound to feel grafted on in some respects.
I think the problem with shipping separate macros that require manual imports is a lack of discoverability, and still working with consumers that want to #[macro_use] extern crate log; to bring everything into scope. On the other hand, we could punt entirely on macro syntax for this RFC, and just not surface structured logging in the current macros for now. Then we could experiment with new macros externally.

@KodrAus I tend to agree with @Ralith and I do think info!(kv; "format", arg; arg) is awkward.
If that is what results in the final design there are decent odds I'd end up making a wrapper crate with different syntax and I'd rather not proliferate a logging API idiolect if I can help it.

Ok, I think it's probably better not to ship macro support that users don't like, since we can work on it externally, and frameworks like tokio-trace and slog that are already structured can integrate with log without log's macros surfacing its structured logging support.

Alrighty, I've updated the RFC text to punt on the macro syntax.
I'd really like to be able to spend some time exploring different macro designs that are structured off-the-bat. There's still tangible benefits to shipping a structured logging API in log even without macro support out-of-the-box by enabling better integration with existing structured frameworks, and opening up the design space for the community to bootstrap new log! macros that suit their needs. So I'm not suggesting we never add structured support to the log! macros in log, I'm suggesting we just ship what we need now to unblock experimenting with new macros externally.
What do you all think?

@Ralith do you find the info!(kv; "format", arg; arg) style awkward? If so, what is it you don't like?

I meant to contrast it to taking structure from named format arguments directly, as in the info!("the format string with a {number}"; number = 2); example. Being forced to duplicate arguments for them to appear in both the message and the structured data would be unfortunate.

I'm suggesting we just ship what we need now to unblock experimenting with new macros externally.

That seems like a reasonable idea.

Alrighty, this has been running for a quite a while now, and thanks to everyone that's been involved so far it seems like we've worked through the major parts of the design space.
cc @rust-lang-nursery/libs and the community: shall we kick off a final comment period for this one? If you'd like to register a concern with the RFC please leave a comment here! If we haven't received any by Friday, 8th of March then I'll merge this one and work towards PRing the initial minimal API proposed in the text.
I appreciate that this is an enormous document, and the implications of various design decisions are pretty well buried at this point. So any PRs that introduce structured logging support will be small and incremental, so we have an opportunity to discuss them just-in-time too. I personally consider this RFC more of a guide than a commitment.

I wanted to add in a little plug for something that the serilog package in .NET does that has been really useful when sifting through log messages, and that is to allow logging of the message template without substitution. As a structured log, it becomes immensely easy to do a search for the template, particularly if values haven't already been substituted in. Is there a way that we could do this and also give a logging backend access to the original message template? (Not sure if this would be something in this RFC, or maybe a different one, but structured logs and templates work really well together.)

@neoeinstein Sure! That would be a feature of the log! macro to capture the format as a template and/or as a fully interpolated message. Serilog's templates conform to a standard that an alternative log macro implementation built on top of log could also use instead of Rust's standard formatting syntax if it wanted to. That template could then be added as an ordinary key-value pair with a special name (like @mt).

Merged!
Thanks everybody that's given their time and input so far ❤ Now we'll start building up the implementation incrementally in log itself.
We'll tag these PRs with a GitHub label so you can find them, but log is fairly low traffic so watching this repository shouldn't generate too much noise.

@KodrAus I realise I'm very late to the party, but would the API proposed by this RFC supported logging nested values? The use case I'm thinking of is when I have a HashMap representing an HTTP request's headers, and want to log those under a "headers" key. I couldn't see any mention of nested values in the RFC, and also couldn't infer whether or not they'd be possible to support.

Hi @atroche :wave:
Ah yes that scenario is actually supported through external serialization APIs. So, for example, the current unstable implementation supports a framework called sval. So you can capture a HashMap like:
use log::kv::Value;

// Assuming `Header` implements `sval::Value`
let map: HashMap<String, Header> = request.headers();

// `Value` implements `sval::Value`, so we can visit complex structure
let value = Value::from_sval(&map);

// If `Header` implements `serde::Serialize` instead then we can still use it
let map = sval::serde::to_serialize(map);
let value = Value::from_sval(&map);

We haven't spent much time on how we might surface that through the macros just yet though.


variables seem to be underlining when hovering over them

Type: Bug
very sporadically when hovering mouse cursor over words(i believe all instances have been with variables) the word or variable will be underlined for a brief moment and then the underline will go away. if i try to reproduce right after it does not do it again, I am almost certain i have not held command down by mistake on these ocations so it is not the go to definition being activated and if it is then it is malfunctioning and activating without me holding down command
VS Code version: Code 1.72.2 (Universal) (d045a5eda657f4d7b676dedbfa7aab8207f8a075, 2022-10-12T22:16:30.254Z)
OS version: Darwin x64 21.6.0
Modes:
Sandboxed: No
Extensions (1)

Extension
Author (truncated)
Version

fluent-icons
mig
0.0.18

/gifplease

Thanks for reporting this issue! Unfortunately, it's hard for us to understand what issue you're seeing. Please help us out by providing a screen recording showing exactly what isn't working as expected. While we can work with most standard formats, .gif files are preferred as they are displayed inline on GitHub. You may find https://gifcap.dev helpful as a browser-based gif recording tool.
If the issue depends on keyboard input, you can help us by enabling screencast mode for the recording (Developer: Toggle Screencast Mode in the command palette). Lastly, please attach this file via the GitHub web interface as emailed responses will strip files out from the issue.
Happy coding!

This issue has been closed automatically because it needs more information and has not had recent activity. See also our issue reporting guidelines.
Happy Coding!


Update Bayes Rules env

Per
https://github.com/pymc-devs/resources/issues/185

@cast42 Thanks again for the initial PR. Just FYI biggest change is using pymc instead of pymc3!

@canyon289  Thanks for helping me out. Glad I could help.


Accommodate partial OSCAL documents in trestle workspace

Issue description / feature objectives
trestle import and trestle validate should accommodate partial OSCAL documents. For example, a partial SAR document may be comprised of just the results subtree. This would allow for tanium-to-oscal and osco-to-oscal transformer produced documents to be validly stored in trestle workspace.
The ability to permit partials during import and validate could be controlled with a flag, such as --partials-permitted.

Completion Criteria
trestle import  and trestle validate permit partial OSCAL documents.

With the underlying pydantic models this his highly problematic when run w.r.t the existing commands.
I can see a few solutions:

task does dump code into the appropriate location and uses generators to insert the minimum necessary 'replace me' content as appropriate (not elegant, however, it works)

Any partial objects are put outside the schema objects

If this is the case we would need some form of custom object validation (e.g. which does not presume trestle directory structures).
`trestle schema-validate -e 'element path' -f ./file.json


update responsibleai and raiwidgets to erroranalysis 0.5.4

Description
update responsibleai and raiwidgets to erroranalysis 0.5.4
Checklist

[ ] I have added screenshots above for all UI changes.
[ ] I have added e2e tests for all UI changes.
[ ] Documentation was updated if it was needed.

Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 94.51%. Comparing base (b5195ac) to head (0678563).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2558      +/-   ##
==========================================
+ Coverage   87.58%   94.51%   +6.92%
==========================================
  Files         144       18     -126
  Lines        8055      419    -7636
==========================================
- Hits         7055      396    -6659
+ Misses       1000       23     -977

Flag
Coverage Δ

unittests
94.51% <ø> (+6.92%)
:arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.


Sorting breaks when listObj is created from .js file

It's such an odd bug and I haven't been able to figure out why it happens, but when you do
var listObj = new List('whatever', options);

from an included file rather than inside a script tag inside the same html file, the sort buttons only work the first time. When clicked afterwards said button doesn't toggle the list to the opposite order. Any ideas why?

I had something similar happen to me. What was happening was the sort was being toggled twice on click. I was able to track down where the second sort was happening and remove it. Are the classes (asc and desc) being added to the sort button when it's clicked?

No, it doesn't change the class at all, it seems.  But that by itself should not cause such a weird glitch, specifically with sorting... everything else works just fine.

This seems like a duplicate of https://github.com/javve/list.js/issues/316
I'll have a look at that bug!


DRAFT revise node properties

on-going draft to revise graph node properties.
ONE-vscode-DCO-1.0-Signed-off-by: SaeHie Park<EMAIL_ADDRESS>

for #1386

done


snowflake: copy_into correctly parses stage names without parens

COPY INTO should not assume that parens surround Snowflake's stage names.

Pull Request Test Coverage Report for Build<PHONE_NUMBER>
Details

9 of 9 (100.0%)  changed or added relevant lines in 2 files are covered.
No unchanged relevant lines lost coverage.
Overall coverage increased (+0.004%) to 87.874%

Totals

Change from base Build<PHONE_NUMBER>:
0.004%

Covered Lines:
20494

Relevant Lines:
23322

💛  - Coveralls


Update kube-rs to 0.57 and k8s-openapi to 0.12

This has some breaking changes for downstream users but will allow us to treat Crds generic
https://github.com/clux/kube-rs/blob/master/CHANGELOG.md
https://github.com/Arnavion/k8s-openapi/blob/master/CHANGELOG.md

Thanks. Do you think we should merge it now?
It'll probably require changes in all operators.

Yes maybe we wait until we have the other bigger changes merged from product config etc.


Teams fx migration samples

@Harikrishnan-MSFT - Please run build pipeline and share the result.

https://github.com/OfficeDev/Microsoft-Teams-Samples/actions/runs/5641334002


Vue JS 2.0 support?

Looks like things like coerce were removed in VueJS 2.0.

@fareez-ahamed coerce was removed, now it has to be computed. ready was removed, have to use mount now. https://vuejs.org/v2/guide/migration.html#ready-replaced.
I can provide a pull request if you don't have the time.

Keeping the repo in sync with my blog post. So I'm not updating this. Probably will create a fork in future.


router-link triggers navigation with no user interaction

Version
2.4.0
Reproduction link
https://jsfiddle.net/addyjj/e2Ltj7h6/
Steps to reproduce

Create a route that redirects to 'google.com'.
Add a route-link that points to route created in step 1.
On page load, the redirect code is executed even though the link was never clicked on.

What is expected?
Redirect code shouldn't execute unless user clicks on link.
What is actually happening?
Redirect code of router-link is executed on page load.

I used chrome. If you run the fiddle with console open you can see the redirect code is being executed without clicking on the link.

I thought there was an open issue about this but I couldn't find it 😆

The redirect function need to be called to render router-link.

Yeah, that's true, it allows to resolve the actual link to be displayed on the a element
Thanks for tuning in 😄

Thanks for the explanation. I guess what I was really trying to do was navigate to an external link, but I wanted to hide the ugly external login URL behind a clean looking "/login" URL.
    {
      path: '/login',
      name: 'login',
      redirect: to => {
        window.location = store.getters['auth/loginUrl']
        return '/'
      }
    }

Maybe the following request is what I am looking for:
#1280 (Feature request: enable some way of handling external links by router.)
Thanks for your time!

It is a common case to have some links that have been removed and you want to act like a 301 redirection.
We add a htaccess file but we also need to handle it for the links in Vue app.

@lbineau If you talk about routes links being removed, you can simply change the routes deifnition to redirect to an error page, right?
@jeremy-addy Yes, you usecase would be solved with #1280
So I will close this as a ducplicate, ok?

@LinusBorg In my case I talk about pages that have moved from my website to another one (not even a vuejs website). So I needed a way to keep them in the routes object because I use  in templates and redirect them to the proper external url.
That's why I was talking about "301 redirection" in issue (that will solve my problem too I think) https://github.com/vuejs/vue-router/issues/1280

Closing as this is outdated and there's no actionable thing to do
for a redirection, adding a meta option to the record that needs to be redirected and then handling the logic in a beforeEach should work


Support kwargs in exported functions by doing pytree swizzling

This has been resolved by using the torch.export.export API as part of 2.3.


Removed the extra GSoC and JIIT mentions

Closes #17
Removed duplicates like GSoC and JIIT.
Please let me know if any changes are to be made.

Hi @25011908vardhan there is a merge conflict kindly fix it.

I have fixed it. Can you check it now?


Categorized Wontolla percussion, started adding self-made sample pack

I created a Drums/Kicks folder tree for the sample library to be included with LMMS.
Here's some proof for the kick (to show it was self-made):

Of course, the new sample provided (kick01.wav) is hereby licensed under the CC0 1.0 Universal (CC0 1.0)  License
Also, kick samples from "Wontolla percussion" (as they are legally cleared, but unsorted) are moved to the same Drums/Kicks folder.
I'm working on more samples as well, requests are very much welcome.
Preview of the sample (kick01.wav):
https://clyp.it/etpcc3uw?token=10dd70965c99208aa750df528f55d59a

Why hello there.  It's nice to see somebody else here. I checked out your kick, and there appears to be a sizeable portion of predelay before the actual kick. You can trim that out down to the last sample with audacity. Also, all new samples are going to be stereo flacs, exported at the highest quality. I'd appreciate it if you cloned my samples repo https://github.com/SirBothersome/samples so that we're not doing the same stuff twice. Good luck
(@tresf, should this be merged with #25 ?)

Any reason for FLAC (apart from "just because we can")? WAV is the standard for samples, eg. on Windows FLAC requires a codec to be installed up till Windows 10, and with 50kB files it's not like we're gaining much by using compression.

Also, the kick01.wav from repo has no predelay at all, it's probably because of 128kbps MP3 conversion after uploading to Clyp. Here's the waveform for it:

Any reason for FLAC (apart from "just because we can")?

Admin decided that. I don't have the time to find the issue reference ATM, but basically the quality is better.

It can't be. WAV is a one-to-one copy, just a stream of bits as they come in. FLAC is just compressing that stream with lossless compression. You get exactly the same quality (unless you tell FLAC to downgrade for better file size).

Any reason for FLAC

Consistency, really.  Larger samples can benefit and it offers a unified format.

FLAC requires a codec to be installed up till Windows 10

Same argument can be made for OGG, really.  The native OS support isn't overly relevant so as long as the DAW shims with playback ability out of the box.

I'd appreciate it if you cloned my samples repo https://github.com/SirBothersome/samples so that we're not doing the same stuff twice. Good luck
(@tresf, should this be merged with #25 ?)

Actually, I think that multiple PR's can be a really nice solution for this problem, as long as all of us communicate. Doing this in bite sizes and avoiding monster PR's is the way to go imho

Actually, I think that multiple PR's can be a really nice solution for this problem, as long as all of us communicate. Doing this in bite sizes and avoiding monster PR's is the way to go imho

@Umcaruje, If you say so. I was actually saying that because #25 already contains similar kicks. I just wanted @QrchackOfficial to see what I've done so that we aren't making redundant work.

I created a Drums/Kicks folder tree for the sample library to be included with LMMS.

@QrchackOfficial, I think it would be better to have folder trees of sample collections a la Hydrogen. (e.g. acoustic samples together, 808 samples together, electronic samples together etc.) IMO, there's more functionality there.

@SirBothersome Possibly, there's still the issue of things like Electro directory having everything plus the kitchen sink. Maybe at least put it in Drums, then Electric/Acoustic? So we could have bass oneshots and whatnot separately.

@SirBothersome Possibly, there's still the issue of things like Electro directory having everything plus the kitchen sink

That's basically how our current directory system is. I've yet to hear anyone complain.

@QrchackOfficial, can I message you via G+? This is going to involve a lot of discussion methinks.

I hardly ever use G+. Email/Twitter/Slack/Skype would be the way to get to me (in descending reply time order)

Closing this. Repo has been largely restructured, and I have not heard from @QrchackOfficial in a bit on the new samples,