-
Notifications
You must be signed in to change notification settings - Fork 0
Log vs Log T
This library gives you two loggers: the non-generic Log and the generic Log<T>. They
share a name on purpose — exactly like ILogger and ILogger<T> — but they are not
interchangeable. This page explains the difference, why it exists, and how to choose.
| You want… | Use | Message style |
|---|---|---|
Structured {placeholder} fields you can filter on |
Log |
Templates |
| The class + method an entry came from | Log<T> |
Plain strings |
// Log — message templates; named values become JSON fields:
Log.Information("User {userId} logged in from {ip}", "asda", "10.0.0.1");
// {"…","message":"User asda logged in from 10.0.0.1","userId":"asda","ip":"10.0.0.1"}
// Log<T> — class & method context, captured for free:
Log<OrderService>.Information("Processing started");
// {"…","class":"OrderService","method":"Process","message":"Processing started"}A {placeholder} passed to Log<T> is logged literally — Log<T> does not perform
template substitution:
Log<OrderService>.Information("User {userId} logged in", 42);
// message: "User {userId} logged in" ← NOT substituted
// and `42` is treated as the caller-info argument, not a template valueIf you need template fields, use Log. If you need class/method context, use Log<T>. You
cannot get both from a single call — see Why? below.
| Field | Log |
Log<T> |
|---|---|---|
message (with {placeholder} substitution) |
✅ | ❌ (plain string) |
{placeholder} → JSON fields |
✅ | ❌ |
class |
❌ | ✅ typeof(T).Name
|
method |
❌ | ✅ [CallerMemberName]
|
timestamp, level, icon, traceId, exception
|
✅ | ✅ |
Both loggers share the same backend, the same output formatting, the same trace scope, and the same configuration. The only difference is whether class/method are supplied.
There are two ways to capture a method name in .NET:
-
[CallerMemberName]— the compiler bakes the caller's method name in as a default argument value. Zero runtime cost, no stack walk, no PDBs. This is whatLog<T>uses. - Stack walk — inspect the call stack at runtime. Costly, and degrades as the stack deepens. This library deliberately avoids it.
To use [CallerMemberName], the method needs a trailing optional parameter:
public static void Information(string message, [CallerMemberName] string? method = null)But the non-generic Log already spends its trailing slot on the message-template values:
public static void Information(string message, params object?[] args)A params array and a defaulted [CallerMemberName] parameter can't coexist — C# won't let
an optional parameter follow params, and even if it could, the params array would swallow
the caller-info argument. So the design splits cleanly:
-
Logkeepsparams object?[] args→ message templates, no caller info. -
Log<T>takes the caller-info slot → class/method, plain-string messages.
Log<T> doesn't need [CallerMemberName] for the class (it has typeof(T).Name for free)
— it only uses it for the method.
Log (arity 0) and Log<T> (arity 1) are genuinely distinct CLR types that happen to share a
short name. This mirrors the convention every .NET developer already knows — ILogger /
ILogger<T> — so Log and Log<T> read as "one logging concept, optionally typed," and
IntelliSense surfaces both when you type Log.
The trade-off (and the reason for the gotcha above) is that, unlike ILogger<T> — which is
just ILogger plus a category and has identical methods — our Log<T> has a different
contract from Log. Same name, different capabilities. Keep the table at the top in mind and
you won't be caught out.
UseClassName(false) and UseMethodName(false) suppress those fields:
Log.Configure(o => o.UseMethodName(false));
Log<OrderService>.Information("started");
// [OrderService] started ← class kept, method suppressedThese two toggles only affect
Log<T>. The non-genericLognever captures a class or method, so they are no-ops for it.
- Logging a business/structured event with values to filter on →
Log. - Logging from inside a service/handler class where the source location matters →
Log<T>.
Thushar.Logging · a zero-dependency static logger for .NET
📦 NuGet · 💻 GitHub · 🐛 Issues · 📸 Instagram
dotnet add package Thushar.Logging
Getting around
Features
Using it
Under the hood