API Reference
Source of truth:
OzaLog/OzaLog/LOG.cs, generated XML doc shipped in the NuGet package asfile.xml.All public types live in the
OzaLognamespace.
1. LOG static class
The single entry point for all logging. No instantiation, no LoggerFactory, no dependency injection.
using OzaLog;
LOG.Info_Log("Hello, OzaLog!");
1.1 LogLevel methods
For each LogLevel value (Trace / Debug / Info / Warn / Error / Fatal), five overloads are provided. Naming convention: <Level>_Log.
// String message
LOG.Info_Log(string message);
// Toggle file write (true=write, false=console-only when EnableConsoleOutput=true)
LOG.Info_Log(string message, bool writeTxt);
// Formatted message with {0}/{1}/... placeholders
LOG.Info_Log(string message, string[] args, bool writeTxt = true, bool immediateFlush = false);
// Object — automatically serialized to JSON
LOG.Info_Log<T>(T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;
// Object with header message
LOG.Info_Log<T>(string message, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;
Replace Info with Trace, Debug, Warn, Error, or Fatal for the corresponding level.
Automatic immediate flush
Error_Log and Fatal_Log always trigger synchronous immediate flush regardless of the immediateFlush argument. This ensures crash logs reach disk before the process dies. Other levels respect the immediateFlush argument (default false).
Object overload behavior
- When
obj is Exceptionandlevel >= Warn, the object is serialized viaExceptionHandler.CreateSerializableException(...), which recursively expandsInnerException,Datadictionary,StackTrace, and reflected non-standard properties. - Otherwise serialization goes through
System.Text.JsonwithWriteIndented=false,DefaultIgnoreCondition=WhenWritingNull,Encoder=UnsafeRelaxedJsonEscaping.
1.2 CustomName_Log — per-bucket file
Routes the log line to a custom filename instead of the level-based default. Useful for per-symbol files in trading (BTC_Log.txt, ETH_Log.txt).
LOG.CustomName_Log(string name, string message);
LOG.CustomName_Log(string name, string message, bool writeTxt);
LOG.CustomName_Log(string name, string message, string[] args, bool writeTxt = true, bool immediateFlush = false);
LOG.CustomName_Log<T>(string name, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;
LOG.CustomName_Log<T>(string name, string message, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;
→ File path: {baseDir}/{LogPath}/{yyyyMMdd}/{CustomPath}/{name}_Log.{ext}
1.3 LOG.Configure(...) — one-time configuration
public static void Configure(Action<LogConfiguration.LogOptions> configure);
Not re-entrant. Second call throws InvalidOperationException("OzaLog 已初始化(Configure 不可重入)"). If Configure is never called, the first log write auto-initializes with default settings.
1.4 LOG.GetCurrentOptions() — read-only config view
public static LogConfiguration.ILogOptions GetCurrentOptions();
Returns a read-only wrapper around the live LogOptions. Useful for diagnostics and verifying the active configuration.
2. LOG.Quote(...) — Quote pipeline (v3.1+)
The Quote pipeline is an independent async pipeline for high-frequency tick/quote data, separate from the main logger. Field names align with the Binance REST API 24hr Ticker schema.
Prerequisite: enable the pipeline at configuration time:
LOG.Configure(o => o.ConfigureQuote(q => q.Enable = true));Without
Enable = true, allLOG.Quote(...)calls are silent no-ops (no background thread is started).
2.1 A2 core API — struct overload
public static void Quote(in QuoteRecord record);
Zero-allocation enqueue. Validates record synchronously on the calling thread and throws ArgumentException for:
SymbolorBucketis null or emptyExtrasandExtrasJsonboth setExtrascontains a key matching a reserved field (see §2.4)
2.2 A1 convenience overloads
Internally construct a QuoteRecord and delegate to the struct overload.
// Minimal tick — last price only
LOG.Quote(string symbol, string bucket, long ticks, decimal last);
// With bid/ask
LOG.Quote(string symbol, string bucket, long ticks,
decimal last, decimal bid, decimal ask);
// With bid/ask + sizes
LOG.Quote(string symbol, string bucket, long ticks,
decimal last,
decimal bid, decimal bidQty,
decimal ask, decimal askQty);
// Full ticker — aligned with Binance REST API /api/v3/ticker/24hr
LOG.QuoteTicker(string symbol, string bucket, long ticks,
decimal last,
decimal? lastQty = null,
decimal? bid = null, decimal? bidQty = null,
decimal? ask = null, decimal? askQty = null,
decimal? open = null, decimal? prevClose = null,
decimal? high = null, decimal? low = null,
decimal? volume = null, decimal? quoteVolume = null);
// Full ticker with custom Extras dictionary
LOG.QuoteTicker(string symbol, string bucket, long ticks,
decimal last,
IReadOnlyDictionary<string, object> extras,
/* same optional fields as above */);
2.3 QuoteRecord (public readonly struct)
public readonly struct QuoteRecord
{
// Required
public readonly string Symbol; // e.g. "BTCUSDT"
public readonly string Bucket; // e.g. "binance_spot"
public readonly long Ticks; // event time, caller-supplied
public readonly decimal Last; // last trade price
// Optional (all decimal?)
public readonly decimal? LastQty; // qty of last trade
public readonly decimal? Bid, BidQty; // best bid + qty
public readonly decimal? Ask, AskQty; // best ask + qty
public readonly decimal? Open, PrevClose;
public readonly decimal? High, Low;
public readonly decimal? Volume; // cumulative base asset volume
public readonly decimal? QuoteVolume; // cumulative quote asset volume
// Custom fields — mutually exclusive
public readonly IReadOnlyDictionary<string, object>? Extras;
public readonly string? ExtrasJson; // pre-serialized JSON object string
}
Field-name mapping to Binance /api/v3/ticker/24hr response:
QuoteRecord | Binance JSON | Note |
|---|---|---|
Last | lastPrice | required |
LastQty | lastQty | quantity of the most recent trade |
Bid / BidQty | bidPrice / bidQty | best bid quote |
Ask / AskQty | askPrice / askQty | best ask quote |
Open / PrevClose / High / Low | openPrice / prevClosePrice / highPrice / lowPrice | session stats |
Volume | volume | 24h base asset volume |
QuoteVolume | quoteVolume | 24h quote asset volume |
2.4 Reserved Extras keys
The following keys are reserved by the built-in schema. Putting them in Extras (Dictionary) throws ArgumentException synchronously. Putting them in ExtrasJson (string) throws asynchronously inside the dispatcher (logged to console; record is dropped).
ts,symbol,bucket,last,lastQty,bid,bidQty,ask,askQty,open,prevClose,high,low,volume,quoteVolume,extras
2.5 Filename rules
{baseDir}/{LogPath}/{yyyyMMdd}/{QuotePath}/{Bucket}_{Symbol}_Quote.{ext}
- No nested subdirectories:
Bucketbecomes a filename prefix, not a folder. - Auto-sanitization: file-system-invalid characters (
/ \ : * ? " < > |) inSymbol/Bucketare replaced with-in the filename only. The original strings are preserved in the file content. - Day rollover, LRU eviction, and size-based splitting (
_part2_Quote.{ext}etc.) all work the same as the main logger but with their own independentQuoteFileStreamPool.
3. LogLevel enum
public enum LogLevel
{
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5,
CustomName = 99, // used internally by LOG.CustomName_Log(...)
}
v3.0 renamed
CostomName→CustomName(typo fix, breaking change).LOG.CustomName_Log(...)method was always spelled correctly.
4. LogOutputFormat enum (v3.1+)
Selects the main logger output format. Set via LogOptions.OutputFormat.
public enum LogOutputFormat
{
Txt = 0, // human-readable text, .txt extension (default)
Log = 1, // same content as Txt, .log extension
Json = 2, // NDJSON (one JSON object per line), .json extension
}
4.1 Json format schema (NDJSON)
{"ts":1715587425123,"lv":"Info","nm":"","tid":12,"tn":"MainThread","msg":"hello","data":{...}}
| Field | Type | Always present? | Meaning |
|---|---|---|---|
ts | long (epoch_ms) | yes | event timestamp in milliseconds since Unix epoch |
lv | string | yes | "Trace" / "Debug" / "Info" / "Warn" / "Error" / "Fatal" / "CustomName" |
nm | string | yes | log name (CustomName value, or empty for level-based logs) |
tid | int | iff ShowThreadId=true | calling thread's ManagedThreadId |
tn | string | iff ShowThreadName=true AND Thread.Name != null | calling thread's Thread.Name |
msg | string | yes | message text (always emitted even if empty) |
data | object | iff present | parsed JSON of the object-overload payload (Exception or arbitrary object) |
5. QuoteOutputFormat enum (v3.1+)
Selects the Quote pipeline output format. Set via LogOptions.QuoteOptions.OutputFormat.
public enum QuoteOutputFormat
{
Txt = 0, // human-readable key=value, .txt extension (default)
Log = 1, // same content as Txt, .log extension
Json = 2, // NDJSON, .json extension
}
5.1 Txt / Log format
[2026-05-13 10:23:45.123] binance_spot BTCUSDT last=60123.5 bid=60123.0 ask=60124.0 bidQty=0.5 askQty=1.2
- ISO 8601 timestamp prefix (human-readable)
- Null optional fields are skipped (variable line length)
Extrasdictionary entries are appended as morek=vpairs
5.2 Json format (NDJSON)
{"ts":1715587425123,"symbol":"BTCUSDT","bucket":"binance_spot","last":60123.5,"bid":60123.0,"ask":60124.0,"extras":{"funding":0.0001}}
ts= epoch_ms (consistent with main logger)- Only non-null fields are emitted
Extrasis nested under"extras"(not flattened into top level) — keeps a clean schema boundary- Quote NDJSON never includes
tid/tn(Quote represents market events, not program-internal events)
6. Read-only configuration view
LogConfiguration.ILogOptions current = LOG.GetCurrentOptions();
Console.WriteLine(current.OutputFormat); // LogOutputFormat
Console.WriteLine(current.TimeFormat); // "HH:mm:ss.fff" etc.
Console.WriteLine(current.HighPrecisionTimestamp); // bool
Console.WriteLine(current.QuoteOptions.Enable); // bool
See Configuration for the full list of properties.
7. Exception serialization (SerializableExceptionInfo)
When you log an Exception at Warn level or higher via Warn_Log<T>(ex) / Error_Log<T>(ex) / Fatal_Log<T>(ex), the runtime expands it into:
class SerializableExceptionInfo
{
string Type; // ex.GetType().FullName
string Message;
string Source;
string HelpLink;
string StackTrace;
Dictionary<string, string> Data; // expanded from ex.Data
SerializableExceptionInfo InnerException; // recursive
Dictionary<string, string> AdditionalProperties; // reflected non-standard props
}
The result is JSON-serialized into the log line (or the data field in Json output mode).
8. Versioning notes
- v3.1 additions are strictly additive — no public API was removed or renamed.
- All new options on
LogOptionsand the newQuoteOptionsdefault to v3.0 behavior — existing code continues to work unchanged. - The new
ILogOptionsinterface members (OutputFormat,TimeFormat,ShowThreadId,ShowThreadName,HighPrecisionTimestamp,QuoteOptions) are read-only; library consumers normally only readLOG.GetCurrentOptions(), so this is not a breaking change for typical use.