using System.Diagnostics.Metrics; public sealed class EvidenceLog : ILoggerProvider { private readonly object sync=new(); private readonly Queue messages=new(); public string[] Snapshot() { lock(sync) return messages.ToArray(); } public ILogger CreateLogger(string categoryName)=>new Writer(this,categoryName); public void Dispose() { } private void Add(string category,string text) { if(category=="ProductionPrivacy") lock(sync) { if(messages.Count==32) messages.Dequeue(); messages.Enqueue(text); } Console.WriteLine(text); } private sealed class Writer(EvidenceLog owner,string category):ILogger { public IDisposable? BeginScope(TState state) where TState:notnull=>null; public bool IsEnabled(LogLevel level)=>category=="Microsoft.Hosting.Lifetime"||category=="ProductionPrivacy"; public void Log(LogLevel level,EventId id,TState state,Exception? exception,Func formatter) { if(IsEnabled(level)) owner.Add(category,formatter(state,exception)); } } } public sealed class PrivacySignals : IDisposable { private readonly Meter meter=new("SharpRune.ProductionPrivacy"); private readonly MeterListener listener=new(); private readonly Counter counter; private readonly object sync=new(); private readonly HashSet labels=new(StringComparer.Ordinal); private int disposed; public PrivacySignals() { counter=meter.CreateCounter("production.operations"); listener.InstrumentPublished=(instrument,value)=> { if(instrument.Meter.Name=="SharpRune.ProductionPrivacy") value.EnableMeasurementEvents(instrument); }; listener.SetMeasurementEventCallback((instrument,value,tags,state)=> { foreach(var tag in tags) if(tag.Key=="operation"&&tag.Value is string label) lock(sync) labels.Add(label); }); listener.Start(); } public string Record(string raw) { string operation=raw is "quote" or "search" ? raw : "other"; counter.Add(1,new KeyValuePair("operation",operation)); return operation; } public string[] Labels() { lock(sync) return labels.OrderBy(x=>x,StringComparer.Ordinal).ToArray(); } public void Dispose() { if(Interlocked.Exchange(ref disposed,1)!=0)return; listener.Dispose(); meter.Dispose(); } }