Skip to content
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

Setting Global (Variables) Context before logger creation #81

Open
thomasjoscht opened this issue Feb 17, 2015 · 14 comments
Open

Setting Global (Variables) Context before logger creation #81

thomasjoscht opened this issue Feb 17, 2015 · 14 comments

Comments

@thomasjoscht
Copy link

Hey, trying to use log4net1213 with CL 3.0 and feature GlobalVariablesContext does not work in my scenario. For my log4net configuration in app.config I'm using %property{} for changing log file directory dynamically in my bootstrapper. Here part of my app.config:

<log4net>
<appender name="FileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="%property{LogsDirectory}\\Log_" />
</appender>
</log4net>

In combination with log4net specific code all works fine:

log4net.GlobalContext.Properties["LogsDirectory"] = "C:\\Logs";
var logger = Common.Logging.LogManager.GetLogger("MyLogger");

But using new GlobalVariablesContext feature like following code will not work:

var logger = Common.Logging.LogManager.GetLogger("MyLogger");
logger.GlobalVariablesContext.Set("LogsDirectory","C:\\Logs");

This happens propably because of setting global context after logger creation. But how can GlobalContext changed before logger creation with CL 3.0?

@sbohlen
Copy link
Member

sbohlen commented Feb 17, 2015

I'll try to look into this shortly for you; it may be that you've uncovered a scenario that's not properly supported by the new GobalVariablesContext feature (yet!).

@DannyVarod
Copy link

Hi Thomas,

Have you checked if this works when using Log4Net directly (without Common.Logging)?

Perhaps since the log is created prior to the variable being set, Log4Net ignores the change in value, since the log file has already been created.

The Common.Logging GlobalContext is only a thin wrapper above Log4Net's "log4net.GlobalContext", so the behaviour should not differ between direct usage of Log4Net vs usage via Common.Logging.

Regards,
Danny.

@meixger
Copy link
Contributor

meixger commented Feb 20, 2015

TBH i never used properties for log4net logfile configuration.

If you want to use a (global context) property in the log4net configuration you must set it prior to the log4net configuration.

// this works
log4net.GlobalContext.Properties["LogsDirectory"] = "C:\\Logs";
log4net.Config.XmlConfigurator.Configure();
var logger = log4net.LogManager.GetLogger(typeof(MyLogger));

// this doesn't work
log4net.Config.XmlConfigurator.Configure();
log4net.GlobalContext.Properties["LogsDirectory"] = "C:\\Logs";
var logger = log4net.LogManager.GetLogger(typeof(MyLogger));

Common.Logging configures log4net on the first call to Common.Logging.LogManager.GetLogger().

To access the GlobalVariablesContext we now need to call GetLogger() for an instance of an ILog. This triggers log4net configuration and because we haven't yet set the property it will be read as ´(null)´

One (dirty) way i see is re-do log4net configuration after setting GlobalVariablesContext by calling Reset:

var dummy = Common.Logging.LogManager.GetLogger("dummy");

// Reset sets ILoggingFactoryAppender to null so log4net needs to be re-configured
Common.Logging.LogManager.Reset(); 

dummy.GlobalVariablesContext.Set("LogsDirectory", "C:\\Logs");

// Access to LogManager.GetLogger() triggers log4net initialization because the ILoggingFactoryAppender was set to NULL by LogManager.Reset().
var logger = Common.Logging.LogManager.GetLogger<MyLogger>();

@sbohlen
Copy link
Member

sbohlen commented Feb 20, 2015

@meixger, thanks for weighing in here. My read of your research into this seems to imply that this functionality is actually broken for Log4Net support of GlobalVariablesContext in general (e.g., not only for log file location), right?

Just thinking out loud without actually reading the code in Common.Logging (yet), I wonder if we could encapsulate the call to LogManager.Reset() into the .GlobalVariables.Set(...) method so that callers would be none-the-wiser that this was what we were doing (?)

Thoughts welcome (as always!)

@DannyVarod
Copy link

@meixger @sbohlen Does this apply to all usage of the global variables context, or only if used in the log path?

Perhaps additional methods of the form:

Common.Logging.LogManager.GetXVariablesContext() would be useful in this case.

Then you can do this:

var globalVariablesContext = Common.Logger.LogManager.GetGlobalVariablesContext();
globalVariablesContext.Set("LogsDirectory", @"C:\Logs");

var log = Common.Logger.LogManager.GetLogger<CurrentClass>();

A workaround is to define the path variables via the Log4Net/NLog config.

@sbohlen
Copy link
Member

sbohlen commented Feb 20, 2015

I like that a lot more; not only clear your intent but also much less 'hacky' of a way to solve it.

-----Original Message-----
From: "DannyVarod" notifications@github.com
Sent: ‎2/‎20/‎2015 9:15 AM
To: "net-commons/common-logging" common-logging@noreply.github.com
Cc: "Steve Bohlen" sbohlen@gmail.com
Subject: Re: [common-logging] Setting Global (Variables) Context before loggercreation (#81)

@meixger @sbohlen Does this apply to all usage of the global variables context, or only if used in the log path?
Perhaps an additional method of the form:
Common.Logging.LogManager.GetXVariablesContext() which returns an interface container the logger contexts without the actual log method would be useful in this case.
Then you can do this:
var globalVariablesContext = Common.Logger.LogManager.GetGlobalVariablesContext();
globalVariablesContext.Set("LogsDirectory", @"C:\Logs");
var log = Common.Logger.LogManager.GetLogger();

Reply to this email directly or view it on GitHub.

@meixger
Copy link
Contributor

meixger commented Feb 20, 2015

@sbohlen @DannyVarod No, this functionality is actually broken for Log4Net support of GlobalVariablesContext only when used for configuring an appender e.g. log path.

It works fine when those global variables are used within log data:

//log4net.config
<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger - %property{RuntimeValue} - %message%newline" />
</layout>

var logger = Common.Logging.LogManager.GetLogger("MyLogger");
log4net.GlobalContext.Properties["RuntimeValue"] = "foo1";
logger.Info("bar1");
log4net.GlobalContext.Properties["RuntimeValue"] = "foo2";
logger.Info("bar2");

// logging resulted as expected
2015-02-20 16:49:38,317 [1] INFO  MyLogger - foo1 - bar1
2015-02-20 16:49:38,331 [1] INFO  MyLogger - foo2 - bar2

PS: as @DannyVarod suggested, defining the path variables via the Log4Net/NLog config would allow configuring dynamic log path:

log4net.GlobalContext.Properties["LogsDirectory"] = "somedir";
var logger = Common.Logging.LogManager.GetLogger("MyLogger");

@DannyVarod
Copy link

@meixger - as I assumed.

So you can configure the appender via config file and for log parameters, the global variables context does work.

If there is need to affect the configuration of the appender via code, then my second suggestion would work, however, this really should count as a new feature request - not a bug fix, when you consider priorities.

@thomasjoscht
Copy link
Author

Thanks for your investigation of this behavior. The resolution is that a new feature is necessary for direct access to GlobalContext for appender configuration, isn't it? Like this:

Common.Logger.LogManager.GlobalVariablesContext.Set("LogsDirectory","C:\\Logs");

How does the scheduling of priorities for new features look like resp when will this feature appr. be implemented? Thanks for your help.

@sbohlen
Copy link
Member

sbohlen commented Feb 25, 2015

I concur with @DannyVarod 's POV that this should be slated for a new feature/enhancement as its a use-case for GlobalVaraiablesContext that wasn't strictly speaking considered when the feature was developed. I've flipped this to 'enhancement' now for that reason.

The recommendation for the time being is going to have to be to employ the 'hack' suggested by @meixger in #81 (comment).

re: timing for a more elegant solution to this (perhaps along the lines of what is suggested by @DannyVarod in #81 (comment)), this is probably dependent upon our evaluation re: whether or not this can be affected without introducing a breaking-change to the Common.Logging APIs.

Our present plan is to release a Common.Logging 3.1 update in the next week or so that folds in support for NLog 3.2. If this new feature/enhancement can be affected without a need to introduce a breaking change, then it can (probably) fall into that release scope as well. However, if a breaking change will result from this, then its likely to be longer. We just created a rather significant hardship for Common.Logging adopters with the breaking changes in 3.0 (see #74) and since NuGet is still unable to protect consumers from breaking changes, we're less likely to introduce additional breaking changes again (e.g., a 4.0 release to support this feature) this quickly.

If anyone is interested in exploring whether this enhancement can be affected without a breaking change to the API necessitating a 4.0 release, we do take pull-requests :)

@thomasjoscht
Copy link
Author

Hey, are there any plans to implement this in near future? I'm still using the following hack which makes direct referencing of log4net assembly necessary:

log4net.GlobalContext.Properties["LogsDirectory"] = "somedir";

@thomasjoscht
Copy link
Author

Hey, sorry for my spamming, but does no way exists to configure the path for logfiles of log4net with common logging at start/run time without using log4net assembly directly? I couldn't configure it hardcoded in configuration file because the logpath will depend on user account using my application. Thx

@sbohlen sbohlen added this to the Version 4.0 milestone Aug 1, 2017
@Redytel-iTUC
Copy link

use the command ...

log4net.GlobalContext.Properties["LogsDirectory"] = "somedir";

in different appart class but in the same project

@paulinacarolina
Copy link

I'm trying to make a dependency injection for the server, directory and filename, any thoughts or recommendations?
Ive tried with the autofac but Im still unable to create it succesfully, tx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants