-
Notifications
You must be signed in to change notification settings - Fork 936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Logging refactoring #1377
Logging refactoring #1377
Conversation
I think we should utilize |
Using |
Sorry, I don't follow. |
I mean, that when your application is using log4net or NLog (pre 4.5) with a structured log sink, then in your logging system, the name each of the parameters shows as '0', '1', '2', etc. If you were using Serilog or Microsoft's _logger.LogInformation("Getting item {ID} at {RequestTime}", id, DateTime.Now); For output at the logging server, I use Seq (see example of logged message), but I'm sure it's similar in most other systems such as Application Insights or Elasticsearch. As for not adding more methods to the interface, it might actually be beneficial to collapse it down to just a few methods that have the level passed in as a parameter and do the per-level logging with extension methods. The Microsoft's Alternatively, we could just directly rely on |
Anyways, if |
f432b14
to
36b7800
Compare
I've moved most logging methods to extension methods so anything can be done with them in the future without breaking clients implementing custom logging. The interface in now just this: public interface IInternalLogger2
{
void Log(InternalLogLevel logLevel, InternalLogValues state, Exception exception);
bool IsEnabled(InternalLogLevel logLevel);
} The state object is built in the extension methods and it would be straightforward to use |
65b33b8
to
52a8232
Compare
Nice! log4net can use named attributes which can then be exposed to appenders such as app insights for richer analytics, so maybe this ticket should be 'structured logging' rather than 'modern logging', but nitpicking aside, it's a great addition to the base logging in nhib.👍 |
52a8232
to
a40f28e
Compare
a40f28e
to
51e3005
Compare
51e3005
to
53b82fd
Compare
I have a concern that this change can bring performance regression. Could you please check and doublecheck, that this is not the case? |
The build times on TeamCity are equivalent, so they don't show any major change. I wrote some performance tests that simulate 20 million log calls and timed them repeatedly for a statistically valid sample. Each test consists of making 20 million log calls, half formatted, and counting the characters in a mock logger (381 MiB worth). var iterationCount = 10000000;
for (int i = 0; i < iterationCount; i++)
{
logger2.Warn("message");
logger2.Warn("message with parameters {0}, {1}", "string", 5);
} This takes If the 20 million log calls are all disabled (and the formatting and counting don't take place), then the calls took These times are normalized to 95 percent confidence numbers. See logger analysis.xlsx. I did notice that the times are slightly helped by making |
53b82fd
to
e5f8ca8
Compare
Expect implementations of INHibernateLoggerFactory and obsolete ILoggerFactory.
It is somewhat related, since the reason they are adding it is to make interfaces more change friendly. If they go ahead with it, they will change the runtime, according to Mads Torgensen. |
This expectation would be valid if we were doing a logging framework, but we are using logging, and so that expectation does not apply here. It might be exposed as whatever we please as it is not intended by the consumers (except by, probably, extension libraries as Caches). Using the same interface which a client implemented feels wrong. Major logging frameworks do segregate "internal" logger and "public" logger. log4net for example: "public contract" that looks exctly as IIniternalLogger and "private contract" which looks exactly as new INHibernateLogger. The only exception that in our case these contracts are reversed. |
Let's not implement things based on some hope, assumptions or predictions for some future. My gut feeling says that there will be no default methods for the interfaces in the next 5 years.
Apparently, it is not as demonstrated by this PR.
At this point, we have an opportunity to improve things, trying to hold back does not make it better. If we are doing this change, it needs to be designed and implemented properly (with a compatibility level of course). |
No, about |
…vel and NHibernateLogValues
So in summary, these are the changes I've made:
The There can always be more learning the future. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good enough for me. You may wish to do some adjustments as I propose in comments.
src/NHibernate/Logging.extensions.cs
Outdated
logger.Log(NHibernateLogLevel.Fatal, new NHibernateLogValues(format, args), null); | ||
} | ||
|
||
public static void Fatal(this INHibernateLogger logger, string format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit strange to have a format
parameter without argument. Maybe would be better to rename it message
for this no args overload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
src/NHibernate/Logging.cs
Outdated
/// <summary> | ||
/// Instantiates a new instance of the <see cref="NHibernateLogValues"/> structure. | ||
/// </summary> | ||
/// <param name="format">a composite format string</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For casing and punctuation consistency, should be A composite format string.
.
f3b5649
Minor typo and parameter name adjustments made. Merge with master done. This can be squash merged since I haven't been rebasing it. |
Where is |
@mxmissile You're talking about an example, and the target for that example was .net core 2.0: nhibernate-core/src/NHibernate.Example.Web/Infrastructure/NHibernateToMicrosoftLogger.cs Lines 28 to 31 in ac39173
The available extension methods for the public void Log(NHibernateLogLevel logLevel, NHibernateLogValues state, Exception exception)
{
_msLogger.Log(MapLevels[logLevel], exception, state.Format, state.Args);
} |
Using structured log processing can greatly benefit application development. Most logging libraries support sinks that can handle the structured nature of log messages. This means each of the parameters in a
string.Format()
can be become meaningful. The .NET Core logger, NLog, Serilog, and others support sinking to structured logging systems.This is how the NHibernate.Example.Web logging can look with the changing values colored to be noticeable (this is the default settings for the Serilog console sink):

The legacy
IInternalLogger
is directly modeled after the log4net interface and limits the interaction with these modern logging systems. For example, any logging of an exception precludes also passing the string.Formatparams
array into the logging system.There were also many areas that either string concatenation was taking place to generate the log message or string.Format was called then a single string passed into the logger. These have been fixed.
This pull request adds the ability to implement and set an alternative
INHibernateLoggerFactory
which then returns implementations of the newIInternalLogger2
interface. It is not a breaking change for applications that are providing a customILoggerFactory
andIInternalLogger
implementation. The old factory will still be accepted.There are some obsolete extension methods that are intended to catch any merges that are using the old logging function style with the exception as the second parameter. They can probably be removed at some point, because they generate an error if referenced.