Skip to content

Commit 9c367fd

Browse files
committed
更新
1 parent c3c6c70 commit 9c367fd

8 files changed

Lines changed: 1676 additions & 54 deletions

File tree

docs/en/api.md

Lines changed: 317 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,327 @@
11
---
22
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.
44
---
55

66
# API Reference
77

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.
911
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
11164
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+
---
15322

16-
## `LogOptions`
323+
## 8. Versioning notes
17324

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

Comments
 (0)