From: Ronald of S. <aus...@ye...> - 2009-02-18 13:46:10
|
This is the condensed code of my application just for demonstrating my problem: use strict; use warnings; use Log::Log4perl qw(:easy); use Log::Log4perl::Appender::File; Log::Log4perl->easy_init({file=>'STDOUT',layout => '%d{HH:mm} %m%n', level=>$DEBUG}); my $logger=Log::Log4perl::Appender::File->new(filename => "dummy.log", mode => 'clobber'); $logger->log(message=>"test\n"); $logger->layout(Log::Log4perl::Layout::PatternLayout->new('%c %C %m%n')); $logger->log(message => "abc"); $logger->logdebug('xyz'); Reason for this probably slightly unusual way of using Log::Log4perl is the following: My application uses one common logging facility (hence the easy_init). Certain parts of the application want to create occasionally their various other logfiles with their own layout and shorter lifetime than the common logging system (demonstrated here by the variable $logger). Running this program results in a file dummy.log containing test abc and the message Can't call method "logdebug" on an undefined value Now my questions: (1) Why does the second call to log() not obey the pattern layout I have defined the line before? After all, a Appender::File inherits from Appender, and hence layout() should have the desired effect. (2) Why does the error message say "on an undefined value", when $logger (the object where method is called on) is obviously not undefined? (3) I guess one of my mistakes is that Appender::File is not a suitable logger type for my purpose. Should I use a different logger instead, and if yes, which one? Ronald -- Ronald Fischer <aus...@ye...> There are 10 types of people in the world: those who understand binary and those who don't. |
From: Mike S. <m...@pe...> - 2009-02-19 00:07:46
|
On Wed, 18 Feb 2009, Ronald of Steiermark wrote: > Reason for this probably slightly unusual way of using Log::Log4perl > is the following: Hi Ronald, indeed, you're using Log4perl in a very unusual, not to say confusing way. What you think is a 'logger' is really an appender in Log4perl lingo. An appender has no concept of a level like 'debug', only loggers do. An appender only has a log() method, and it will normally log the message you're passing to it unconditionally. The reason why you get the error message 'on an undefined value' is an internal autoload mechanism within the appender that's not defined in your case. The error message is confusing and needs to be fixed, what it should say instead is that logdebug() doesn't exist. Ideally, you would integrate your logging needs into the main log4perl configuration -- can you explain in more detail why this isn't possible? -- Mike Mike Schilli m...@pe... > My application uses one common logging facility (hence the easy_init). > Certain parts of the application want to create occasionally their > various other logfiles with their own layout and shorter lifetime than > the common logging system (demonstrated here by > the variable $logger). This is the condensed code of my application > just for demonstrating my problem: > > use strict; > use warnings; > use Log::Log4perl qw(:easy); > use Log::Log4perl::Appender::File; > Log::Log4perl->easy_init({file=>'STDOUT',layout => '%d{HH:mm} %m%n', > level=>$DEBUG}); > my $logger=Log::Log4perl::Appender::File->new(filename => "dummy.log", > mode => 'clobber'); > $logger->log(message=>"test\n"); > $logger->layout(Log::Log4perl::Layout::PatternLayout->new('%c %C > %m%n')); > $logger->log(message => "abc"); > $logger->logdebug('xyz'); > > > Running this program results in a file dummy.log containing > > test > abc > > and the message > > Can't call method "logdebug" on an undefined value > > Now my questions: > > (1) Why does the second call to log() not obey the pattern layout I have > defined the line before? > After all, a Appender::File inherits from Appender, and hence layout() > should have the desired > effect. > > (2) Why does the error message say "on an undefined value", when $logger > (the object where method is > called on) is obviously not undefined? > > (3) I guess one of my mistakes is that Appender::File is not a suitable > logger type for my purpose. > Should I use a different logger instead, and if yes, which one? > > Ronald > -- > Ronald Fischer <aus...@ye...> > > There are 10 types of people in the world: those who understand binary and those who don't. > > > ------------------------------------------------------------------------------ > Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA > -OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise > -Strategies to boost innovation and cut costs with open source participation > -Receive a $600 discount off the registration fee with the source code: SFAD > http://p.sf.net/sfu/XcvMzF8H > _______________________________________________ > log4perl-devel mailing list > log...@li... > https://lists.sourceforge.net/lists/listinfo/log4perl-devel > |
From: Ronald F. <yn...@mm...> - 2009-02-19 09:16:36
|
> What you think is a 'logger' is really an appender in Log4perl lingo. An > appender has no concept of a level like 'debug', only loggers do. I see, and that's why it also can't understand about layouts. > The reason why you get the error message 'on an undefined value' is an > internal autoload mechanism within the appender that's not defined in > your case. The error message is confusing and needs to be fixed, what it > should say instead is that logdebug() doesn't exist. I see. > Ideally, you would integrate your logging needs into the main log4perl > configuration -- can you explain in more detail why this isn't possible? Maybe it is and I just don't understand Log4perl well enough. We basically have an application which is running permanently (kind of a request server). It accepts requests, processes them, and returns the results. The processing of a single requests typically takes between a couple of minutes, not more than a few hours. The whole application logs its activity to a logfile and to stdout. This is what we have so far, and it is implemented via easy_init (which is, well, easy to use, but maybe not flexible enough): Log::Log4perl->easy_init( {level => $log_level, file => 'STDOUT', layout => '%.1p %d{HH:mm} %M(%L) %m%n' }, {level => $log_level, file => ">$logfile", layout => '%.1p %d{dd.MM. HH:mm:ss (EEE)} %M(%L) %m%n' } ); Now I would like to incorporate the possibility that in addition to that normal, continually going logfile, the logs occuring to each request should be written into a separate, request-specific logfile, so that, if we hav 100 requests on a day, we would end up having 100 extra logfiles in addition to the main logfile. Each of these extra logfiles is named after the request. This means that whenever a new request arrives, we have to create a new logfile. Since the requests are handled in parallel (but non-preemtive, i.e. no threads involved), we have a central "logging handler" which knows which request is the currently active one, and sends each logging event to the standard log, plus to the request-specific one. Any suggestion how I could implement this? Ronald -- Ronald Fischer <ro...@em...> + If a packet hits a pocket on a socket on a port, + and the bus is interrupted and the interrupt's not caught, + then the socket packet pocket has an error to report. + (cited after Peter van der Linden) |
From: Mike S. <m...@pe...> - 2009-02-20 05:13:28
|
On Thu, 19 Feb 2009, Ronald Fischer wrote: >> What you think is a 'logger' is really an appender in Log4perl lingo. An >> appender has no concept of a level like 'debug', only loggers do. > > I see, and that's why it also can't understand about layouts. Actually, just for the record, you can set an appender's layout: $appender->layout($layout); # perldoc Log::Log4perl::Appender > Now I would like to incorporate the possibility that in addition to > that normal, continually going logfile, the logs occuring to each > request should be written into a separate, request-specific logfile, > so that, if we hav 100 requests on a day, we would end up having 100 > extra logfiles in addition to the main logfile. Add another appender to your category and have it name its log files according to a function get_request_name() that your application provides: log4perl.logger = DEBUG, FooApp, BarApp, AnotherAppender # ... log4perl.appender.AnotherAppender.filename = \ sub { "mylog." . get_request_name() . ".log" } > Each of these extra logfiles is named after the request. This means > that whenever a new request arrives, we have to create a new logfile. > Since the requests are handled in parallel (but non-preemtive, i.e. no > threads involved), we have a central "logging handler" which knows > which request is the currently active one, and sends each logging > event to the standard log, plus to the request-specific one. I'm assuming that each 'request' starts the app anew. The continually going logfile appender is in 'append' mode and the request-based logfile appender is in 'clobber' mode. -- Mike Mike Schilli m...@pe... |
From: Ronald F. <yn...@mm...> - 2009-02-20 09:02:17
|
On Thu, 19 Feb 2009 21:13 -0800, "Mike Schilli" <m...@pe...> wrote: > Actually, just for the record, you can set an appender's layout: > > $appender->layout($layout); # perldoc Log::Log4perl::Appender I tried the following code: $nl = Log::Log4perl::Appender::File->new( filename => $logname, mode => 'clobber', utf8 => 1, create_at_logtime => 1 ); $nl->layout(Log::Log4perl::Layout::PatternLayout->new('%C %m%n')); But when I write something with $nl->log(...), I don't see the layout applied. > > Now I would like to incorporate the possibility that in addition to > > that normal, continually going logfile, the logs occuring to each > > request should be written into a separate, request-specific logfile, > > so that, if we hav 100 requests on a day, we would end up having 100 > > extra logfiles in addition to the main logfile. > > Add another appender to your category and have it name its log > files according to a function get_request_name() that your > application provides: > > log4perl.logger = DEBUG, FooApp, BarApp, AnotherAppender This is valid Perl code????? So I have to the left of the assignment a catenation, and to the right the comma operator? > # ... > log4perl.appender.AnotherAppender.filename = \ > sub { "mylog." . get_request_name() . ".log" } Here too I don't understand the syntax. > I'm assuming that each 'request' starts the app anew. No, the app is running continually during days or weeks in a main loop and polls for new requests. If a request is arriving, it is decomposed into various pieces. Say a request R is coming, followed by request S. The app first is analyzing R and decomposes it into pieces according to some algorithm (let's call the pieces R1, R2, R3). Then it does the next with request S (which might result into pieces S1, S2, S3, S4). Then it schedules the pieces for execution. One schedule might be: R1, S1, S2, R2, S3, S4, R3 and executes them. While some "R"-piece is executed, logs should go to the "R.log" file. While some "S"-piece is executed, logs should go to the "S.log" file. This is a highly simplified example, and of course there are not only 2 requests per day, but many more. Ronald -- Ronald Fischer <ro...@em...> + If a packet hits a pocket on a socket on a port, + and the bus is interrupted and the interrupt's not caught, + then the socket packet pocket has an error to report. + (cited after Peter van der Linden) |
From: Mike S. <m...@pe...> - 2009-02-20 18:41:33
|
On Fri, 20 Feb 2009, Ronald Fischer wrote: > I tried the following code: > $nl = > Log::Log4perl::Appender::File->new( > filename => $logname, > mode => 'clobber', > utf8 => 1, > create_at_logtime => 1 > ); > $nl->layout(Log::Log4perl::Layout::PatternLayout->new('%C %m%n')); That won't work, you have to use Log4perl's appender wrapper around the real appender -- but this is just a theoretical exercise, I don't really recommend playing with Log4perl's internals. >> log4perl.logger = DEBUG, FooApp, BarApp, AnotherAppender > > This is valid Perl code????? So I have to the left of the assignment a > catenation, and to the right the comma operator? Of course not, it's l4p config syntax. >> # ... >> log4perl.appender.AnotherAppender.filename = \ >> sub { "mylog." . get_request_name() . ".log" } > > Here too I don't understand the syntax. The right-hand side of an assignment in l4p config syntax can be a perl code reference. > No, the app is running continually during days or weeks in a main > loop and polls for new requests. If a request is arriving, it is > decomposed into various pieces. Say a request R is coming, followed > by request S. The app first is analyzing R and decomposes it into > pieces according to some algorithm (let's call the pieces R1, R2, > R3). Then it does the next with request S (which might result into > pieces S1, S2, S3, S4). Then it schedules the pieces for execution. > One schedule might be: R1, S1, S2, R2, S3, S4, R3 and executes them. > While some "R"-piece is executed, logs should go to the "R.log" > file. While some "S"-piece is executed, logs should go to the > "S.log" file. That's alright, just a subroutine like get_request_name() shown above accordingly, so that it returns a unique name for every unique request or subrequest. -- Mike Mike Schilli m...@pe... |
From: Ronald F. <yn...@mm...> - 2009-02-23 08:53:14
|
On Fri, 20 Feb 2009 10:41 -0800, "Mike Schilli" <m...@pe...> wrote: > On Fri, 20 Feb 2009, Ronald Fischer wrote: > >> log4perl.logger = DEBUG, FooApp, BarApp, AnotherAppender > > > > This is valid Perl code????? So I have to the left of the assignment a > > catenation, and to the right the comma operator? > > Of course not, it's l4p config syntax. > > >> # ... > >> log4perl.appender.AnotherAppender.filename = \ > >> sub { "mylog." . get_request_name() . ".log" } > > > > Here too I don't understand the syntax. > > The right-hand side of an assignment in l4p config syntax can be a > perl code reference. I see, and I guess get_request_name must be in package main so that the logging package can find it? There are two (hopefully minor) problems left: There are times where there is no request being processed, but still logging occurs. One solution would be to have get_request_name() in this case return a dummy name (say: '_none_'), which would result in a file mylog._none_.log, which I then simply can discard. But maybe you have a more elegant solution for this? The other problem is that I would strongly prefer a solution without l4p configuration file. The reason is that one requirement for our application was "one config file only", so we have an application specific configuration file, so I'm using easy_init to initialize the logging. Although I can clearly see advantages of using a separate config file for logging, it would violate a requirement for this project. Sure, maybe I can convince the customer to change this requirement, but your solution could be done interally, I would be happier. Ronald -- Ronald Fischer <ro...@em...> + If a packet hits a pocket on a socket on a port, + and the bus is interrupted and the interrupt's not caught, + then the socket packet pocket has an error to report. + (cited after Peter van der Linden) |
From: Mike S. <m...@pe...> - 2009-02-23 16:34:25
|
On Mon, 23 Feb 2009, Ronald Fischer wrote: > I see, and I guess get_request_name must be in package main so that > the logging package can find it? Either that or you call the fully qualified name, Foo::Bar::get_request_name. > There are times where there is no request being processed, but still > logging occurs. One solution would be to have get_request_name() in > this case return a dummy name (say: '_none_'), which would result in > a file mylog._none_.log, which I then simply can discard. But maybe > you have a more elegant solution for this? Looks perfect to me. > The other problem is that I would strongly prefer a solution without > l4p configuration file. The reason is that one requirement for our > application was "one config file only", so we have an application > specific configuration file, so I'm using easy_init to initialize the > logging. Although I can clearly see advantages of using a separate > config file for logging, it would violate a requirement for this > project. Sure, maybe I can convince the customer to change this > requirement, but your solution could be done interally, I would be > happier. Everything you can do in a configuration file can be done in code with the Log4perl API, problem is just that it might get confusing if you're trying to figure out what's going on by looking at the configuration file, while application code is pulling the rug under it :). -- Mike Mike Schilli m...@pe... |
From: Ronald F. <yn...@mm...> - 2009-02-24 08:52:14
|
On Mon, 23 Feb 2009 08:34 -0800, "Mike Schilli" <m...@pe...> wrote: > > The other problem is that I would strongly prefer a solution without > > l4p configuration file. The reason is that one requirement for our > > application was "one config file only", so we have an application > > specific configuration file, so I'm using easy_init to initialize the > > logging. > > Everything you can do in a configuration file can be done in code with > the Log4perl API, problem is just that it might get confusing if you're > trying to figure out what's going on by looking at the configuration > file, while application code is pulling the rug under it :). Right, but this is a decision I don't want to (actually, I must not) decide by myself, but this must be done by the project leader, which means that I have to at least demonstrate both ways. Thanks to your explanations, I understand how to do it in a config file, but I don't see how to do it in my code. Probably it is not possible at all with easy_init (which I would like to continue to use) - at least I can't find anything suitable in the perldoc of Log::Log4perl. There is something in the section "Advanced Perl Configuration", but I don't see how to use it in my case. This is how far I got: # This is my current initialization Log::Log4perl->easy_init( # ... Details left out ); # Now define the request-specific logger use Log::Log4perl::Layout; use Log::Log4perl::Level; my $req_logger= Log::Log4perl->get_logger(???); my $req_appender = Log::Log4perl::Appender->new( "Log::Log4perl::Appender::File", name => "???", mode => 'clobber', utf8 => 1, create_at_logtime => 1 filename => \&get_current_logfile); # ??? $req_appender->layout(Log::Log4perl::Layout::PatternLayout->new("...")); $req_logger->add_appender($req_appender); $req_logger->level($INFO); And when it comes to logging something to the request logger, I would write i.e. $req_logger->loginfo("my message goes here"); I don't know whether the general idea is right or whether there is a shorter way to write it, but still there are a few open points, which I have marked in my code above with ???. First, what category string should I pass to get_logger? We don't work with categories, and we have none defined with easy_init, so it would make sense to me to pass the "default category". Could this be done by passing, say, the null string? Second, what is the "name" parameter used for when creating the Appender. I found the usage of name => in the perldoc, but what's its purpose? Finally, is my usage of passing a code reference to filename correct? Kind regards, Ronald > > -- Mike > > Mike Schilli > m...@pe... -- Ronald Fischer <ro...@em...> + If a packet hits a pocket on a socket on a port, + and the bus is interrupted and the interrupt's not caught, + then the socket packet pocket has an error to report. + (cited after Peter van der Linden) |