[{"data":1,"prerenderedAt":1026},["ShallowReactive",2],{"content-en-changelog":3},{"doc":4,"debug":1023},{"id":5,"title":6,"body":7,"description":1015,"extension":1016,"meta":1017,"navigation":1018,"path":1019,"seo":1020,"stem":1021,"__hash__":1022},"content\u002Fen\u002Fchangelog.md","Changelog",{"type":8,"value":9,"toc":984},"minimark",[10,14,35,38,47,57,62,67,118,123,167,172,306,311,332,336,405,409,492,494,501,510,513,559,562,618,622,631,633,639,643,716,719,794,797,829,833,867,870,916,918,925,928,949,952,966,969,977,979],[11,12,6],"h1",{"id":13},"changelog",[15,16,17,18,22,23,26,27,34],"p",{},"This file tracks all notable changes to the ",[19,20,21],"strong",{},"OzaLog"," package (formerly ",[19,24,25],{},"Ozakboy.NLOG",").\nVersion numbers follow ",[28,29,33],"a",{"href":30,"rel":31},"https:\u002F\u002Fsemver.org\u002F",[32],"nofollow","Semantic Versioning",".",[36,37],"hr",{},[39,40,42,46],"h2",{"id":41},"_310-2026-05-14",[43,44,45],"span",{},"3.1.0"," - 2026-05-14",[48,49,50],"blockquote",{},[15,51,52,53,56],{},"Three new capabilities: customizable time\u002Fthread display, configurable output format (txt\u002Flog\u002Fjson), and a dedicated ",[19,54,55],{},"Quote"," pipeline for high-frequency tick\u002Fquote data with Binance-aligned schema. All additions are backward compatible — defaults preserve v3.0 behavior.",[58,59,61],"h3",{"id":60},"added","Added",[15,63,64],{},[19,65,66],{},"Customizable Time & Thread Display",[68,69,70,82,106],"ul",{},[71,72,73,77,78,81],"li",{},[74,75,76],"code",{},"LogOptions.TimeFormat"," (default ",[74,79,80],{},"\"HH:mm:ss.fff\"",") — free-form .NET DateTime format string for the message prefix. Falls back to default on parse failure.",[71,83,84,77,87,90,91,77,94,97,98,101,102,105],{},[74,85,86],{},"LogOptions.ShowThreadId",[74,88,89],{},"true",") and ",[74,92,93],{},"LogOptions.ShowThreadName",[74,95,96],{},"false",") — independently toggle thread ID \u002F name in the prefix. When ",[74,99,100],{},"ShowThreadName=true"," but the calling thread has no name (",[74,103,104],{},"Thread.Name == null","), the entire thread segment is omitted.",[71,107,108,77,111,113,114,117],{},[74,109,110],{},"LogOptions.HighPrecisionTimestamp",[74,112,96],{},") — opt-in ",[74,115,116],{},"Stopwatch","-hybrid mode that reconstructs µs-level precision from the 1ms cache; raises caller-side ticks read cost from ~5ns to ~30ns.",[15,119,120],{},[19,121,122],{},"Multiple Output Formats",[68,124,125,150],{},[71,126,127,77,130,133,134,137,138,141,142,145,146,149],{},[74,128,129],{},"LogOptions.OutputFormat",[74,131,132],{},"LogOutputFormat.Txt",") — global format selector: ",[74,135,136],{},"Txt"," \u002F ",[74,139,140],{},"Log"," (same content, different extension) \u002F ",[74,143,144],{},"Json"," (NDJSON with fixed schema ",[74,147,148],{},"{ts, lv, nm, tid?, tn?, msg, data?}",").",[71,151,152,153,156,157,156,160,156,163,166],{},"JSON timestamps emit as epoch_ms integers. Field names use short forms (",[74,154,155],{},"lv",", ",[74,158,159],{},"nm",[74,161,162],{},"tid",[74,164,165],{},"tn",") for compactness.",[15,168,169],{},[19,170,171],{},"Quote (Tick\u002FTicker) Pipeline",[68,173,174,224,234,270,288,295],{},[71,175,176,179,180,183,184,187,188,156,191,156,194,156,197,156,200,156,203,156,206,156,209,156,212,156,215,156,218,156,221,149],{},[74,177,178],{},"LOG.Quote(...)"," and ",[74,181,182],{},"LOG.QuoteTicker(...)"," — public API for high-frequency quote\u002Fticker data with field names aligned to ",[19,185,186],{},"Binance REST 24hr Ticker"," schema (",[74,189,190],{},"Last",[74,192,193],{},"LastQty",[74,195,196],{},"Bid",[74,198,199],{},"BidQty",[74,201,202],{},"Ask",[74,204,205],{},"AskQty",[74,207,208],{},"Open",[74,210,211],{},"PrevClose",[74,213,214],{},"High",[74,216,217],{},"Low",[74,219,220],{},"Volume",[74,222,223],{},"QuoteVolume",[71,225,226,229,230,233],{},[74,227,228],{},"QuoteRecord"," (public ",[74,231,232],{},"readonly struct",") — A2 core API for zero-allocation enqueue. Convenience A1 overloads for the common cases (tick only \u002F bid+ask \u002F full ticker \u002F ticker+extras).",[71,235,236,239,240,243,244,247,248,251,252,255,256,259,260,156,263,156,266,269],{},[74,237,238],{},"QuoteOptions"," (opt-in via ",[74,241,242],{},"opt.ConfigureQuote(q => q.Enable = true)",", default off) — independent async pipeline with its own dispatcher, queue, and ",[74,245,246],{},"FileStreamPool",". Configurable ",[74,249,250],{},"OutputFormat"," (Txt\u002FLog\u002FJson), ",[74,253,254],{},"MaxOpenStreams"," (default 500), ",[74,257,258],{},"MaxQueueSize"," (default 50000), ",[74,261,262],{},"MaxBatchSize",[74,264,265],{},"FlushIntervalMs",[74,267,268],{},"OnDropped(long)"," callback.",[71,271,272,275,276,279,280,283,284,287],{},[74,273,274],{},"QuoteRecord.Extras"," (",[74,277,278],{},"IReadOnlyDictionary\u003Cstring, object>",") for flexible attributes and ",[74,281,282],{},"QuoteRecord.ExtrasJson"," (raw pre-serialized JSON string for the zero-overhead path) — mutually exclusive; setting both throws ",[74,285,286],{},"ArgumentException"," at the call site.",[71,289,290,291,294],{},"File naming: ",[74,292,293],{},"{baseDir}\u002F{LogPath}\u002F{yyyyMMdd}\u002F{QuotePath}\u002F{Bucket}_{Symbol}_Quote.{ext}"," — no nested subdirectories.",[71,296,297,298,301,302,305],{},"Symbol\u002Fbucket sanitization: file-system-invalid characters (",[74,299,300],{},"\u002F \\ : * ? \" \u003C > |",") are automatically replaced with ",[74,303,304],{},"-"," in filenames; the original symbol\u002Fbucket text is preserved in the file content.",[15,307,308],{},[19,309,310],{},"Tests",[68,312,313,316],{},[71,314,315],{},"Four new xUnit test files covering custom time formats, NDJSON formatting, Quote schema\u002Ferror scenarios, and filename sanitization (48 tests total, all passing).",[71,317,318,321,322,325,326,325,329,149],{},[74,319,320],{},"OzaLog.Test\u002FProgram.cs"," rewritten to a comprehensive v3.1 smoke-test covering every API surface and error path in a single linear run, with optional CLI args for format selection (",[74,323,324],{},"txt","\u002F",[74,327,328],{},"log",[74,330,331],{},"json",[58,333,335],{"id":334},"improved","Improved",[68,337,338,348,372,390],{},[71,339,340,343,344,347],{},[74,341,342],{},"LogItem"," carries ",[74,345,346],{},"ThreadName"," so the dispatcher thread can render the calling thread's name (previously unavailable post-enqueue).",[71,349,350,351,354,355,325,358,361,362,364,365,367,368,371],{},"All Quote API overloads funnel through ",[74,352,353],{},"LOG.Quote(in QuoteRecord)"," for centralized validation. Errors (null\u002Fempty Symbol or Bucket, ",[74,356,357],{},"Extras",[74,359,360],{},"ExtrasJson"," both set, ",[74,363,357],{}," key colliding with a reserved field) throw ",[74,366,286],{}," ",[19,369,370],{},"synchronously"," on the calling thread — not deferred to the dispatcher.",[71,373,374,377,378,381,382,385,386,389],{},[74,375,376],{},"LogFormatter"," retains a fast path for the default ",[74,379,380],{},"HH:mm:ss.fff"," format (hand-written, zero-allocation); other formats route through ",[74,383,384],{},"DateTime.ToString"," with ",[74,387,388],{},"FormatException"," fallback to default.",[71,391,392,394,395,137,398,137,401,404],{},[74,393,246],{}," supports per-output extension (",[74,396,397],{},".txt",[74,399,400],{},".log",[74,402,403],{},".json",") with corresponding part-detection logic for size-based file splitting.",[58,406,408],{"id":407},"technical","Technical",[68,410,411,442,458,478],{},[71,412,413,416,417,420,421,424,425,137,428,431,432,137,435,137,438,441],{},[74,414,415],{},"System.Text.Json",": bumped from ",[74,418,419],{},"8.0.5"," → ",[74,422,423],{},"9.0.16"," for ",[74,426,427],{},"netstandard2.0",[74,429,430],{},"netstandard2.1"," targets (",[74,433,434],{},"net8.0",[74,436,437],{},"net9.0",[74,439,440],{},"net10.0"," still use the BCL built-in — zero NuGet dependencies).",[71,443,444,416,447,420,450,453,454,457],{},[74,445,446],{},"Microsoft.SourceLink.GitHub",[74,448,449],{},"8.0.0",[74,451,452],{},"10.0.300"," (build-only, ",[74,455,456],{},"PrivateAssets=all",", no consumer impact).",[71,459,460,461,156,464,156,467,156,470,473,474,477],{},"New internal types: ",[74,462,463],{},"JsonLogFormatter",[74,465,466],{},"QuoteFormatter",[74,468,469],{},"QuoteFileStreamPool",[74,471,472],{},"QuoteLogHandler",". Quote pipeline runs entirely in parallel with the main ",[74,475,476],{},"AsyncLogHandler"," — they share no locks or stream pools.",[71,479,480,481,137,483,137,485,137,487,137,489,491],{},"Build verified across all 5 TargetFrameworks (",[74,482,427],{},[74,484,430],{},[74,486,434],{},[74,488,437],{},[74,490,440],{},") with 0 errors.",[36,493],{},[39,495,497,500],{"id":496},"_301-2026-05-09",[43,498,499],{},"3.0.1"," - 2026-05-09",[48,502,503],{},[15,504,505,506,509],{},"Metadata + repository improvements release. ",[19,507,508],{},"No library code changes"," — the OzaLog assembly is byte-identical to v3.0.0 (Deterministic build).",[58,511,335],{"id":512},"improved-1",[68,514,515,553],{},[71,516,517,520,521,524,525,528,529,532,533,156,536,156,539,156,542,545,546,549,550,34],{},[19,518,519],{},"NuGet package metadata refreshed",": cleaner ",[74,522,523],{},"Description"," (highlights ",[74,526,527],{},"LOG.Info_Log(\"...\")"," API + HFT pipeline + zero dependencies + crypto tick stream use case), updated ",[74,530,531],{},"PackageTags"," (added ",[74,534,535],{},"ozalog",[74,537,538],{},"hft",[74,540,541],{},"high-performance",[74,543,544],{},"zero-dependency","; removed misleading ",[74,547,548],{},"nlog"," tag), polished ",[74,551,552],{},"Title",[71,554,555,558],{},[74,556,557],{},"PackageReleaseNotes"," now uses absolute GitHub URLs for cross-references (NuGet doesn't render relative paths).",[58,560,408],{"id":561},"technical-1",[68,563,564,574,606,609,612],{},[71,565,566,569,570],{},[19,567,568],{},"New project website",": Nuxt 4 + @nuxt\u002Fcontent + Tailwind CSS, deployed to GitHub Pages → ",[28,571,572],{"href":572,"rel":573},"https:\u002F\u002Fozakboy.github.io\u002FOzaLog\u002F",[32],[71,575,576,579,580,583,584,156,587,590,591,156,594,156,597,156,600,156,603,149],{},[19,577,578],{},"Repository documentation restructured",": all user-facing docs moved to ",[74,581,582],{},"docs\u002F{en,zh-TW}\u002F"," bilingual tree (",[74,585,586],{},"changelog.md",[74,588,589],{},"migration.md",", plus templates for ",[74,592,593],{},"getting-started.md",[74,595,596],{},"configuration.md",[74,598,599],{},"api.md",[74,601,602],{},"async-pipeline.md",[74,604,605],{},"benchmarks.md",[71,607,608],{},"GitHub Actions auto-deploys the site on push to main.",[71,610,611],{},"Sponsor page added with USDT (BEP20) wallet + Binance Pay QR.",[71,613,614,617],{},[74,615,616],{},"uplog"," release flow extended: now also creates GitHub Release and pushes to NuGet.org automatically.",[58,619,621],{"id":620},"notes","Notes",[68,623,624],{},[71,625,626,627,34],{},"For migration from v2.x see ",[28,628,630],{"href":629},".\u002Fmigration","migration guide",[36,632],{},[39,634,636,500],{"id":635},"_300-2026-05-09",[43,637,638],{},"3.0.0",[58,640,642],{"id":641},"breaking-changes","Breaking Changes",[68,644,645,659,674,700],{},[71,646,647,650,651,420,653,655,656,658],{},[19,648,649],{},"Package renamed",": ",[74,652,25],{},[74,654,21],{},". The previous package on NuGet is deprecated and points here. See ",[28,657,630],{"href":629}," for the upgrade guide.",[71,660,661,650,664,420,667,669,670,673],{},[19,662,663],{},"Namespace renamed",[74,665,666],{},"ozakboy.LOG",[74,668,21],{},". All ",[74,671,672],{},"using"," statements in consumer code must be updated.",[71,675,676,679,680,156,683,156,686,689,690,156,692,156,694,156,696,156,698,34],{},[19,677,678],{},"Removed TargetFrameworks",": dropped ",[74,681,682],{},".NET Framework 4.6.2",[74,684,685],{},"net6.0",[74,687,688],{},"net7.0"," (all EOL). Now supports ",[74,691,427],{},[74,693,430],{},[74,695,434],{},[74,697,437],{},[74,699,440],{},[71,701,702,650,705,420,708,711,712,715],{},[19,703,704],{},"Enum typo fixed",[74,706,707],{},"LogLevel.CostomName",[74,709,710],{},"LogLevel.CustomName",". The public method ",[74,713,714],{},"LOG.CustomName_Log(...)"," was already correctly named — only the underlying enum value was renamed.",[58,717,61],{"id":718},"added-1",[68,720,721,728,743,752,764,773,779,785],{},[71,722,723,724,727],{},"HFT-grade async pipeline: ",[74,725,726],{},"ConcurrentQueue\u003Cstruct LogItem>"," + persistent FileStream pool + 1ms cached timestamp + drop-oldest backpressure.",[71,729,730,77,733,735,736,179,739,742],{},[74,731,732],{},"LogOptions.EnableGlobalExceptionCapture",[74,734,96],{},") — opt-in subscription to ",[74,737,738],{},"AppDomain.UnhandledException",[74,740,741],{},"TaskScheduler.UnobservedTaskException",", auto-logs at Fatal level with synchronous immediate flush.",[71,744,745,77,748,751],{},[74,746,747],{},"LogOptions.MaxOpenFileStreams",[74,749,750],{},"100",") — LRU upper bound; exceeding closes the least recently written stream.",[71,753,754,77,757,759,760,763],{},[74,755,756],{},"LogOptions.DiskFlushIntervalMs",[74,758,750],{},") — periodic ",[74,761,762],{},"Flush()"," interval for persistent FileStreams.",[71,765,766,77,769,772],{},[74,767,768],{},"LogOptions.OnDropped",[74,770,771],{},"null",") — callback invoked when the async queue drops the oldest item under backpressure.",[71,774,775,778],{},[74,776,777],{},"OzaLog.Tests\u002F"," — xUnit test project covering concurrency, LRU, day rollover, backpressure, GlobalExceptionCapture toggle, and format correctness.",[71,780,781,784],{},[74,782,783],{},"OzaLog.Benchmarks\u002F"," — BenchmarkDotNet project comparing OzaLog with ZLogger, ZeroLog, and Serilog.",[71,786,787,790,791,793],{},[74,788,789],{},"MIGRATION.md"," — upgrade guide from ",[74,792,25],{}," v2.x.",[58,795,335],{"id":796},"improved-2",[68,798,799,809,820,823,826],{},[71,800,801,802,137,804,137,806,808],{},"Zero NuGet dependencies on ",[74,803,434],{},[74,805,437],{},[74,807,440],{}," (System.Text.Json is built into the BCL on these targets).",[71,810,811,812,815,816,819],{},"Formatting work moved off the calling thread — callers only enqueue the raw tuple ",[74,813,814],{},"(level, name, message, args, ticks, threadId)","; no ",[74,817,818],{},"string.Format"," on the hot path.",[71,821,822],{},"Persistent FileStreams eliminate per-batch open\u002Fclose, reducing syscall cost to near-zero.",[71,824,825],{},"Day rollover handled inline in the dispatcher (compares cached ticks date vs. stream date).",[71,827,828],{},"Expired-log cleanup moved to a background timer (was on the hot path in v2.x).",[58,830,832],{"id":831},"fixed","Fixed",[68,834,835,853],{},[71,836,837,838,841,842,845,846,848,849,852],{},"Double-format bug in ",[74,839,840],{},"LOG.cs"," where ",[74,843,844],{},"Console.WriteLine(formattedMessage, args)"," could throw ",[74,847,388],{}," if the formatted message coincidentally contained ",[74,850,851],{},"{0}","-style tokens.",[71,854,855,856,179,859,862,863,866],{},"Auto-flush level selection — ",[74,857,858],{},"Error",[74,860,861],{},"Fatal"," now correctly trigger immediate flush regardless of the caller's ",[74,864,865],{},"immediateFlush"," argument.",[58,868,408],{"id":869},"technical-2",[68,871,872,880,891,901,907,913],{},[71,873,874,876,877,879],{},[74,875,342],{}," changed from class to ",[74,878,232],{}," — zero GC on the hot path.",[71,881,882,883,886,887,890],{},"New ",[74,884,885],{},"Core\u002FTimestampCache.cs"," — background timer updates ",[74,888,889],{},"volatile long _currentTicks"," every 1ms; callers only read.",[71,892,882,893,896,897,900],{},[74,894,895],{},"Core\u002FFileStreamPool.cs"," — persistent FileStreams keyed by ",[74,898,899],{},"(level, name)"," with LRU eviction.",[71,902,882,903,906],{},[74,904,905],{},"Core\u002FLogRetentionCleaner.cs"," — background expired-log cleanup, off the hot path.",[71,908,882,909,912],{},[74,910,911],{},"Core\u002FGlobalExceptionCapture.cs"," — opt-in global exception subscription.",[71,914,915],{},"Build verified across all 5 TargetFrameworks with 0 warnings \u002F 0 errors.",[36,917],{},[39,919,921,924],{"id":920},"_210-2024",[43,922,923],{},"2.1.0"," - 2024",[58,926,61],{"id":927},"added-2",[68,929,930,933,936,939,946],{},[71,931,932],{},"Added support for .NET 8.0",[71,934,935],{},"Introduced async logging with configurable batch processing",[71,937,938],{},"Added customizable directory structure for different log levels",[71,940,941,942,945],{},"Added support for custom log types (",[74,943,944],{},"CustomName_Log",")",[71,947,948],{},"Added console output support",[58,950,335],{"id":951},"improved-3",[68,953,954,957,960,963],{},[71,955,956],{},"Enhanced file management with automatic log rotation",[71,958,959],{},"Enhanced exception handling and serialization",[71,961,962],{},"Improved configuration system with more options",[71,964,965],{},"Better handling of file paths across operating systems",[58,967,408],{"id":968},"technical-3",[68,970,971,974],{},[71,972,973],{},"Improved thread safety and performance",[71,975,976],{},"Implemented intelligent file size management",[36,978],{},[48,980,981],{},[15,982,983],{},"History prior to 2.1.0 is not fully tracked here. See git history and the NuGet package page for details.",{"title":985,"searchDepth":986,"depth":987,"links":988},"",2,3,[989,995,1001,1009],{"id":41,"depth":986,"text":990,"children":991},"3.1.0 - 2026-05-14",[992,993,994],{"id":60,"depth":987,"text":61},{"id":334,"depth":987,"text":335},{"id":407,"depth":987,"text":408},{"id":496,"depth":986,"text":996,"children":997},"3.0.1 - 2026-05-09",[998,999,1000],{"id":512,"depth":987,"text":335},{"id":561,"depth":987,"text":408},{"id":620,"depth":987,"text":621},{"id":635,"depth":986,"text":1002,"children":1003},"3.0.0 - 2026-05-09",[1004,1005,1006,1007,1008],{"id":641,"depth":987,"text":642},{"id":718,"depth":987,"text":61},{"id":796,"depth":987,"text":335},{"id":831,"depth":987,"text":832},{"id":869,"depth":987,"text":408},{"id":920,"depth":986,"text":1010,"children":1011},"2.1.0 - 2024",[1012,1013,1014],{"id":927,"depth":987,"text":61},{"id":951,"depth":987,"text":335},{"id":968,"depth":987,"text":408},"All notable changes to OzaLog.","md",{},true,"\u002Fen\u002Fchangelog",{"title":6,"description":1015},"en\u002Fchangelog","i6R4HEVXHBL2hVB_Lawv2eXc-krqCvT7Xhv3bGhhOV8",{"matched":1024,"target":1019,"all":1025},"path()",null,1778734456067]