|
1 | 1 | --- |
2 | 2 | title: API Reference |
3 | | -description: Public API of OzaLog (LOG static class, LogOptions, LogLevel). |
| 3 | +description: Complete public API of OzaLog v3.1 — LOG static class, LogOptions, QuoteRecord, enums. |
4 | 4 | --- |
5 | 5 |
|
6 | 6 | # API Reference |
7 | 7 |
|
8 | | -> TODO: write content. Source of truth is `OzaLog/OzaLog/LOG.cs` (XML doc → `file.xml`). |
| 8 | +> Source of truth: [`OzaLog/OzaLog/LOG.cs`](https://github.com/ozakboy/OzaLog/blob/main/OzaLog/OzaLog/LOG.cs), generated XML doc shipped in the NuGet package as `file.xml`. |
| 9 | +> |
| 10 | +> All public types live in the `OzaLog` namespace. |
9 | 11 |
|
10 | | -## `LOG` static class |
| 12 | +--- |
| 13 | + |
| 14 | +## 1. `LOG` static class |
| 15 | + |
| 16 | +The single entry point for all logging. No instantiation, no `LoggerFactory`, no dependency injection. |
| 17 | + |
| 18 | +```csharp |
| 19 | +using OzaLog; |
| 20 | + |
| 21 | +LOG.Info_Log("Hello, OzaLog!"); |
| 22 | +``` |
| 23 | + |
| 24 | +### 1.1 LogLevel methods |
| 25 | + |
| 26 | +For each `LogLevel` value (Trace / Debug / Info / Warn / Error / Fatal), five overloads are provided. Naming convention: `<Level>_Log`. |
| 27 | + |
| 28 | +```csharp |
| 29 | +// String message |
| 30 | +LOG.Info_Log(string message); |
| 31 | + |
| 32 | +// Toggle file write (true=write, false=console-only when EnableConsoleOutput=true) |
| 33 | +LOG.Info_Log(string message, bool writeTxt); |
| 34 | + |
| 35 | +// Formatted message with {0}/{1}/... placeholders |
| 36 | +LOG.Info_Log(string message, string[] args, bool writeTxt = true, bool immediateFlush = false); |
| 37 | + |
| 38 | +// Object — automatically serialized to JSON |
| 39 | +LOG.Info_Log<T>(T obj, bool writeTxt = true, bool immediateFlush = false) where T : class; |
| 40 | + |
| 41 | +// Object with header message |
| 42 | +LOG.Info_Log<T>(string message, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class; |
| 43 | +``` |
| 44 | + |
| 45 | +**Replace `Info` with `Trace`, `Debug`, `Warn`, `Error`, or `Fatal` for the corresponding level.** |
| 46 | + |
| 47 | +#### Automatic immediate flush |
| 48 | + |
| 49 | +`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`). |
| 50 | + |
| 51 | +#### Object overload behavior |
| 52 | + |
| 53 | +- When `obj is Exception` and `level >= Warn`, the object is serialized via `ExceptionHandler.CreateSerializableException(...)`, which recursively expands `InnerException`, `Data` dictionary, `StackTrace`, and reflected non-standard properties. |
| 54 | +- Otherwise serialization goes through `System.Text.Json` with `WriteIndented=false`, `DefaultIgnoreCondition=WhenWritingNull`, `Encoder=UnsafeRelaxedJsonEscaping`. |
| 55 | + |
| 56 | +### 1.2 `CustomName_Log` — per-bucket file |
| 57 | + |
| 58 | +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`). |
| 59 | + |
| 60 | +```csharp |
| 61 | +LOG.CustomName_Log(string name, string message); |
| 62 | +LOG.CustomName_Log(string name, string message, bool writeTxt); |
| 63 | +LOG.CustomName_Log(string name, string message, string[] args, bool writeTxt = true, bool immediateFlush = false); |
| 64 | +LOG.CustomName_Log<T>(string name, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class; |
| 65 | +LOG.CustomName_Log<T>(string name, string message, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class; |
| 66 | +``` |
| 67 | + |
| 68 | +→ File path: `{baseDir}/{LogPath}/{yyyyMMdd}/{CustomPath}/{name}_Log.{ext}` |
| 69 | + |
| 70 | +### 1.3 `LOG.Configure(...)` — one-time configuration |
| 71 | + |
| 72 | +```csharp |
| 73 | +public static void Configure(Action<LogConfiguration.LogOptions> configure); |
| 74 | +``` |
| 75 | + |
| 76 | +**Not re-entrant.** Second call throws `InvalidOperationException("OzaLog 已初始化(Configure 不可重入)")`. If `Configure` is never called, the first log write auto-initializes with default settings. |
| 77 | + |
| 78 | +### 1.4 `LOG.GetCurrentOptions()` — read-only config view |
| 79 | + |
| 80 | +```csharp |
| 81 | +public static LogConfiguration.ILogOptions GetCurrentOptions(); |
| 82 | +``` |
| 83 | + |
| 84 | +Returns a read-only wrapper around the live `LogOptions`. Useful for diagnostics and verifying the active configuration. |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## 2. `LOG.Quote(...)` — Quote pipeline (v3.1+) |
| 89 | + |
| 90 | +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. |
| 91 | + |
| 92 | +> **Prerequisite**: enable the pipeline at configuration time: |
| 93 | +> ```csharp |
| 94 | +> LOG.Configure(o => o.ConfigureQuote(q => q.Enable = true)); |
| 95 | +> ``` |
| 96 | +> Without `Enable = true`, all `LOG.Quote(...)` calls are silent no-ops (no background thread is started). |
| 97 | +
|
| 98 | +### 2.1 A2 core API — struct overload |
| 99 | +
|
| 100 | +```csharp |
| 101 | +public static void Quote(in QuoteRecord record); |
| 102 | +``` |
| 103 | +
|
| 104 | +Zero-allocation enqueue. Validates `record` synchronously on the calling thread and throws `ArgumentException` for: |
| 105 | + |
| 106 | +- `Symbol` or `Bucket` is null or empty |
| 107 | +- `Extras` and `ExtrasJson` both set |
| 108 | +- `Extras` contains a key matching a reserved field (see [§2.4](#24-reserved-extras-keys)) |
| 109 | + |
| 110 | +### 2.2 A1 convenience overloads |
| 111 | + |
| 112 | +Internally construct a `QuoteRecord` and delegate to the struct overload. |
| 113 | + |
| 114 | +```csharp |
| 115 | +// Minimal tick — last price only |
| 116 | +LOG.Quote(string symbol, string bucket, long ticks, decimal last); |
| 117 | + |
| 118 | +// With bid/ask |
| 119 | +LOG.Quote(string symbol, string bucket, long ticks, |
| 120 | + decimal last, decimal bid, decimal ask); |
| 121 | + |
| 122 | +// With bid/ask + sizes |
| 123 | +LOG.Quote(string symbol, string bucket, long ticks, |
| 124 | + decimal last, |
| 125 | + decimal bid, decimal bidQty, |
| 126 | + decimal ask, decimal askQty); |
| 127 | + |
| 128 | +// Full ticker — aligned with Binance REST API /api/v3/ticker/24hr |
| 129 | +LOG.QuoteTicker(string symbol, string bucket, long ticks, |
| 130 | + decimal last, |
| 131 | + decimal? lastQty = null, |
| 132 | + decimal? bid = null, decimal? bidQty = null, |
| 133 | + decimal? ask = null, decimal? askQty = null, |
| 134 | + decimal? open = null, decimal? prevClose = null, |
| 135 | + decimal? high = null, decimal? low = null, |
| 136 | + decimal? volume = null, decimal? quoteVolume = null); |
| 137 | + |
| 138 | +// Full ticker with custom Extras dictionary |
| 139 | +LOG.QuoteTicker(string symbol, string bucket, long ticks, |
| 140 | + decimal last, |
| 141 | + IReadOnlyDictionary<string, object> extras, |
| 142 | + /* same optional fields as above */); |
| 143 | +``` |
| 144 | + |
| 145 | +### 2.3 `QuoteRecord` (public `readonly struct`) |
| 146 | + |
| 147 | +```csharp |
| 148 | +public readonly struct QuoteRecord |
| 149 | +{ |
| 150 | + // Required |
| 151 | + public readonly string Symbol; // e.g. "BTCUSDT" |
| 152 | + public readonly string Bucket; // e.g. "binance_spot" |
| 153 | + public readonly long Ticks; // event time, caller-supplied |
| 154 | + public readonly decimal Last; // last trade price |
| 155 | +
|
| 156 | + // Optional (all decimal?) |
| 157 | + public readonly decimal? LastQty; // qty of last trade |
| 158 | + public readonly decimal? Bid, BidQty; // best bid + qty |
| 159 | + public readonly decimal? Ask, AskQty; // best ask + qty |
| 160 | + public readonly decimal? Open, PrevClose; |
| 161 | + public readonly decimal? High, Low; |
| 162 | + public readonly decimal? Volume; // cumulative base asset volume |
| 163 | + public readonly decimal? QuoteVolume; // cumulative quote asset volume |
11 | 164 |
|
12 | | -- `LOG.Trace_Log(...)` / `Debug_Log` / `Info_Log` / `Warn_Log` / `Error_Log` / `Fatal_Log` |
13 | | -- `LOG.CustomName_Log(name, ...)` |
14 | | -- `LOG.Configure(action)` — call once at startup |
| 165 | + // Custom fields — mutually exclusive |
| 166 | + public readonly IReadOnlyDictionary<string, object>? Extras; |
| 167 | + public readonly string? ExtrasJson; // pre-serialized JSON object string |
| 168 | +} |
| 169 | +``` |
| 170 | + |
| 171 | +**Field-name mapping to Binance `/api/v3/ticker/24hr` response**: |
| 172 | + |
| 173 | +| `QuoteRecord` | Binance JSON | Note | |
| 174 | +|---|---|---| |
| 175 | +| `Last` | `lastPrice` | required | |
| 176 | +| `LastQty` | `lastQty` | quantity of the most recent trade | |
| 177 | +| `Bid` / `BidQty` | `bidPrice` / `bidQty` | best bid quote | |
| 178 | +| `Ask` / `AskQty` | `askPrice` / `askQty` | best ask quote | |
| 179 | +| `Open` / `PrevClose` / `High` / `Low` | `openPrice` / `prevClosePrice` / `highPrice` / `lowPrice` | session stats | |
| 180 | +| `Volume` | `volume` | 24h base asset volume | |
| 181 | +| `QuoteVolume` | `quoteVolume` | 24h quote asset volume | |
| 182 | + |
| 183 | +### 2.4 Reserved Extras keys |
| 184 | + |
| 185 | +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). |
| 186 | + |
| 187 | +> `ts`, `symbol`, `bucket`, `last`, `lastQty`, `bid`, `bidQty`, `ask`, `askQty`, `open`, `prevClose`, `high`, `low`, `volume`, `quoteVolume`, `extras` |
| 188 | +
|
| 189 | +### 2.5 Filename rules |
| 190 | + |
| 191 | +``` |
| 192 | +{baseDir}/{LogPath}/{yyyyMMdd}/{QuotePath}/{Bucket}_{Symbol}_Quote.{ext} |
| 193 | +``` |
| 194 | + |
| 195 | +- **No nested subdirectories**: `Bucket` becomes a filename prefix, not a folder. |
| 196 | +- **Auto-sanitization**: file-system-invalid characters (`/ \ : * ? " < > |`) in `Symbol` / `Bucket` are replaced with `-` **in the filename only**. The original strings are preserved in the file content. |
| 197 | +- Day rollover, LRU eviction, and size-based splitting (`_part2_Quote.{ext}` etc.) all work the same as the main logger but with their own independent `QuoteFileStreamPool`. |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## 3. `LogLevel` enum |
| 202 | + |
| 203 | +```csharp |
| 204 | +public enum LogLevel |
| 205 | +{ |
| 206 | + Trace = 0, |
| 207 | + Debug = 1, |
| 208 | + Info = 2, |
| 209 | + Warn = 3, |
| 210 | + Error = 4, |
| 211 | + Fatal = 5, |
| 212 | + CustomName = 99, // used internally by LOG.CustomName_Log(...) |
| 213 | +} |
| 214 | +``` |
| 215 | + |
| 216 | +> v3.0 renamed `CostomName` → `CustomName` (typo fix, breaking change). `LOG.CustomName_Log(...)` method was always spelled correctly. |
| 217 | +
|
| 218 | +--- |
| 219 | + |
| 220 | +## 4. `LogOutputFormat` enum (v3.1+) |
| 221 | + |
| 222 | +Selects the **main logger** output format. Set via `LogOptions.OutputFormat`. |
| 223 | + |
| 224 | +```csharp |
| 225 | +public enum LogOutputFormat |
| 226 | +{ |
| 227 | + Txt = 0, // human-readable text, .txt extension (default) |
| 228 | + Log = 1, // same content as Txt, .log extension |
| 229 | + Json = 2, // NDJSON (one JSON object per line), .json extension |
| 230 | +} |
| 231 | +``` |
| 232 | + |
| 233 | +### 4.1 Json format schema (NDJSON) |
| 234 | + |
| 235 | +```json |
| 236 | +{"ts":1715587425123,"lv":"Info","nm":"","tid":12,"tn":"MainThread","msg":"hello","data":{...}} |
| 237 | +``` |
| 238 | + |
| 239 | +| Field | Type | Always present? | Meaning | |
| 240 | +|---|---|---|---| |
| 241 | +| `ts` | `long` (epoch_ms) | yes | event timestamp in milliseconds since Unix epoch | |
| 242 | +| `lv` | `string` | yes | `"Trace"` / `"Debug"` / `"Info"` / `"Warn"` / `"Error"` / `"Fatal"` / `"CustomName"` | |
| 243 | +| `nm` | `string` | yes | log name (CustomName value, or empty for level-based logs) | |
| 244 | +| `tid` | `int` | iff `ShowThreadId=true` | calling thread's `ManagedThreadId` | |
| 245 | +| `tn` | `string` | iff `ShowThreadName=true` AND `Thread.Name != null` | calling thread's `Thread.Name` | |
| 246 | +| `msg` | `string` | yes | message text (always emitted even if empty) | |
| 247 | +| `data` | object | iff present | parsed JSON of the object-overload payload (Exception or arbitrary object) | |
| 248 | + |
| 249 | +--- |
| 250 | + |
| 251 | +## 5. `QuoteOutputFormat` enum (v3.1+) |
| 252 | + |
| 253 | +Selects the **Quote pipeline** output format. Set via `LogOptions.QuoteOptions.OutputFormat`. |
| 254 | + |
| 255 | +```csharp |
| 256 | +public enum QuoteOutputFormat |
| 257 | +{ |
| 258 | + Txt = 0, // human-readable key=value, .txt extension (default) |
| 259 | + Log = 1, // same content as Txt, .log extension |
| 260 | + Json = 2, // NDJSON, .json extension |
| 261 | +} |
| 262 | +``` |
| 263 | + |
| 264 | +### 5.1 Txt / Log format |
| 265 | + |
| 266 | +``` |
| 267 | +[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 |
| 268 | +``` |
| 269 | + |
| 270 | +- ISO 8601 timestamp prefix (human-readable) |
| 271 | +- Null optional fields are skipped (variable line length) |
| 272 | +- `Extras` dictionary entries are appended as more `k=v` pairs |
| 273 | + |
| 274 | +### 5.2 Json format (NDJSON) |
| 275 | + |
| 276 | +```json |
| 277 | +{"ts":1715587425123,"symbol":"BTCUSDT","bucket":"binance_spot","last":60123.5,"bid":60123.0,"ask":60124.0,"extras":{"funding":0.0001}} |
| 278 | +``` |
| 279 | + |
| 280 | +- `ts` = epoch_ms (consistent with main logger) |
| 281 | +- Only non-null fields are emitted |
| 282 | +- `Extras` is nested under `"extras"` (not flattened into top level) — keeps a clean schema boundary |
| 283 | +- Quote NDJSON **never** includes `tid` / `tn` (Quote represents market events, not program-internal events) |
| 284 | + |
| 285 | +--- |
| 286 | + |
| 287 | +## 6. Read-only configuration view |
| 288 | + |
| 289 | +```csharp |
| 290 | +LogConfiguration.ILogOptions current = LOG.GetCurrentOptions(); |
| 291 | +Console.WriteLine(current.OutputFormat); // LogOutputFormat |
| 292 | +Console.WriteLine(current.TimeFormat); // "HH:mm:ss.fff" etc. |
| 293 | +Console.WriteLine(current.HighPrecisionTimestamp); // bool |
| 294 | +Console.WriteLine(current.QuoteOptions.Enable); // bool |
| 295 | +``` |
| 296 | + |
| 297 | +See [Configuration](./configuration.md) for the full list of properties. |
| 298 | + |
| 299 | +--- |
| 300 | + |
| 301 | +## 7. Exception serialization (`SerializableExceptionInfo`) |
| 302 | + |
| 303 | +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: |
| 304 | + |
| 305 | +```csharp |
| 306 | +class SerializableExceptionInfo |
| 307 | +{ |
| 308 | + string Type; // ex.GetType().FullName |
| 309 | + string Message; |
| 310 | + string Source; |
| 311 | + string HelpLink; |
| 312 | + string StackTrace; |
| 313 | + Dictionary<string, string> Data; // expanded from ex.Data |
| 314 | + SerializableExceptionInfo InnerException; // recursive |
| 315 | + Dictionary<string, string> AdditionalProperties; // reflected non-standard props |
| 316 | +} |
| 317 | +``` |
| 318 | + |
| 319 | +The result is JSON-serialized into the log line (or the `data` field in Json output mode). |
| 320 | + |
| 321 | +--- |
15 | 322 |
|
16 | | -## `LogOptions` |
| 323 | +## 8. Versioning notes |
17 | 324 |
|
18 | | -See [Configuration](./configuration.md) for the full list. |
| 325 | +- v3.1 additions are **strictly additive** — no public API was removed or renamed. |
| 326 | +- All new options on `LogOptions` and the new `QuoteOptions` default to v3.0 behavior — existing code continues to work unchanged. |
| 327 | +- The new `ILogOptions` interface members (`OutputFormat`, `TimeFormat`, `ShowThreadId`, `ShowThreadName`, `HighPrecisionTimestamp`, `QuoteOptions`) are **read-only**; library consumers normally only read `LOG.GetCurrentOptions()`, so this is not a breaking change for typical use. |
0 commit comments