❔ .Net Logging - Is this valid?
So i saw this nick chapsas short and I've too passed my message as a string(big error) into the logger.
Aka this one:
(60 sec video)https://www.youtube.com/shorts/PvQGVmozCdU
So I've changed my method to do this:
Calling it like this:
Wanted to make sure this is correct and am not shooting myself in the foot
Nick Chapsas
YouTube
The Biggest Lie About the .NET Logger | .NET Tips 7
The .NET Logging API is lying!
55 Replies
well, it can get some improvement, but whats described in the short is implemented.
- i would check with
ILogger.IsEnabled(logLevel)
if it actually makes to further process, if not early return from the method, this way u will avoid some useless overhead (u are creating new instances here after all)
- u already know the final args array size, so create that instead of doing the extra "round trip" via the list
- i think u forgot a this
for the first parameter, so its actually an extension method
- args
could be a params
parameter, so that u do not have to create the array when calling the method urself
- i would add additional methods that take 0 to 3 parameters, so that for these methods u avoid another array instantiationtaking about smth like this?
well, nope, in the overloads u would call the
logger.Log(....)
directly as wellnot sure i caught this one too :
args could be a params parameter, so that u do not have to create the array when calling the method urself
so basically u would need a helper method for
var messageTemplate = "{FileName}:{LineNumber} - {FunctionName}(): " + message;
to keep it drycan you give me a revised example?
u also do not need to introduce generic types here, even if u would pass a struct, it would have to be boxed anyway
wait how do u actually call this?
So you would pass in a object like {LastName=LastName,etc..}
to the
params
:
here exactly the same happens, just that u do not have to type out the new object[]
stuff for passing the parametersI see, didnt know about this
thats nice
ILogger.Log()
also has params object[] args
for its arguments, thus it will automatically create an array (and i think they also have these optimized versions i was just talking about, but not 100% sure rn)
u could also mess with the GetMessageTemplate
a bit i guess, because most of the time u will constant strings passed to ur methods, thus u could probably build a ConcurrentDictionary<string, string>
around it to cache the template creationInteresting will try to implement
so just making sure,
This:
Becomes this:
Seems decent? based on your code
Forget that its not a proper extension method atm, will also adress that later on
yep
Amazing, thx alot bud 😄
wait, quite difficult to say, because of the named templates
better try it and see 😂
for
{0}
and alike it would work for sureMeaning the order can get screwed up?
shouldnt happen if i pass parameters in the correct order no? basic stuff
What am i missing
if u pass them in correct order it should work
and then u can also just call it with
LoggerExtension.LogWithDetails(_logger, LogLevel.Information, "ur template", lesson.Id, lesson.Phases.Count);
but then
I gotta do this :
Im guessing the logger itself calls a tostring on the object properties?
no u dont have to call
ToString()
I do, tells me cannot convert from int to string
wont compile
this the code:
it tries to pass it as
filePath
right now, because u probably do not have the 2 args version nor the params
version yetoh right theres that
Not sure what youre reffering to
the whole thing was talking about basically D:
not sure if any of this works then
Lol
Wait if thats the case
then wtf do i do?
gotcha, makes sense thanks for pointing that out
how would you fix said code to make it work? any workaround? kinda lost
its in the extenion methods, not directly in the
ILogger
interface https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loggerextensions.log?view=dotnet-plat-ext-7.0#microsoft-extensions-logging-loggerextensions-log(microsoft-extensions-logging-ilogger-microsoft-extensions-logging-loglevel-system-string-system-object())LoggerExtensions.Log Method (Microsoft.Extensions.Logging)
Formats and writes a log message at the specified log level.
How does that support my usecase of the caller memebers?
we would still have to mess with the stuff a bit
with most even i think
Yup that what gets me, I cant incorporate that
I gotta go for like 30 mins, will be back, if you come up with smth please write i will check it, very important to me to get this done lol
thx for now mate!
yeah im still looking into it
so, not even something like
log.Log(LogLevel.Information, "example {TestId}", new { TestId = 16 });
works.
it would result in something like
where logger.Log(LogLevel.Information, "example {TestId} {AnotherTest}", new { TestId = 16, AnotherTest = "test" });
would be totally broken and result in a System.AggregateException
.
thats because it resolves to the method i linked earlier. but passing the values as parameters each, does work, but u have to care about the order then and it completely ignores whats inside the {}
so logger.Log(LogLevel.Information, "example {0} {1} {0}", 16, "test");
would break again.
u would have to call logger.Log(LogLevel.Information, "example {0} {1} {0}", 16, "test", 16);
u also can not use the params
variant because it always has to be the last parameter, thus u would not be able to use the [Caller...]
attributes
thus u would have to use something logger.LogWithDetails(LogLevel.Information, "example {} {} {}", new object[] { "hello", "test", 16 });
for calls for which u did not write a method with explicit argument count
so if u implement thisu r still screwed, because it resolves to the wrong methods and thus has wrong parameter count for formatting
so the only real way is to resolve it from the
System.Environment.StackTrace
property. which will be quite annoyingOmfg lol
(note, that the array variant does work)
So basically no solution for this? i gotta drop the members?
actually, u can use the
System.Diagnostics.StackTrace
class to retrieve the info, unlike System.Environment.StackTrace
thats a normal class with properties. so that should be easier
then u can drop the [Caller...]
annotated parameters and it should work. additionally u would then again be able to use the params
so something like this:
but note that it comes from debug symbols, so my simple test program even spit out null and 0 for file name and line numberwouldnt it perhaps be smarter, to just pass those parameters from a single fuction and add them to the array?
why do i need a trace
ffs i simply forgot to pass
true
, so var frame = new StackTrace(true).GetFrame(1);
and at least in debug mode u get some stuff
that should work as well if i understood it correctlyActually no it wouldnt
if i create this method:
and call it from the logwithDetails, then the caller will be from the same file etc
not good at all
@AntiMatter this works:
logger.Log(LogLevel.Information, logger.GetCallSite(), "example {} {} {} {}", "hello", "test", 16, 17);
prints
u could probably even make it a ref struct
so it can not get boxed (tho then u cant use it in an async method anymore)
(and u dont even have to call it LogWithDetails
, as its clear from passing the CallSite
parameter, which method it has to call)does GetMessageTemplate return compile const?
probably not
so thats no good =[
probably depends on
message
but at this point the compiler cant know if its compile constwill try to inject it into the log config/creation
well, i think with a
ConditionalWeakTable<string, string>
(not the ConcurrectDictionary<string, string>
) u can at least avoiding creating multiple instances by using thatTable.GetValue(message, static m => "{}:{} - {}():" + message);
i guess. and iirc the lookup is also O(1), so pretty fast, and it automatically drops it if message
will be GCedwill check it out boss
if any news will update 😄
thx alot!
sure, but im hitting the sack for tonight, 10.45pm and i didnt sleep last night 😂
same here lol mind blown over these logs time to get off
thats what i came up with in the end: https://gist.github.com/cap5lut/593468756bda861f4189217ec1dfd371
i removed the coupling from the call site factory method to the logger, so now its just
CallSite.Get()
, so u could also use it somehwere else.
it would also probably be better to wrap the Path.GetFileName(callSite.FilePath);
in its own method to keep it DRY in case u want to adjust it later somehow
i would also probably add the methods where u dont have to pass the loglevel, like ILogger.LogError(...)
and alike
maybe even just throw it all in a source generator cuz its mainly copy and paste.
ofc the namespaces should also be adjusted, imo CallSite
shouldnt even be in a logging namespace, as it has only indireclty to do with it.Make your log messages unique :)
Then you know where it came from
yeah, where to look should be actually clear by the default provided stuff, eg, the type the logger is for and ofc the distinctive message itself
but was sorta interesting to check how to extend the api surface for that ;p
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.oh just saw it now, will check it out!
I was sure it a bust, did you actually check about the memory allocation stuff by any chance?
nope
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.