﻿<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://purl.org/atom/ns#">
	<link xmlns="http://purl.org/atom/ns#" type="text/html" rel="alternate" href="http://sial.org/blog/" title="Jeremy Mates’s Blog"/>
	<title xmlns="http://purl.org/atom/ns#">Jeremy Mates’s Blog</title>
	<entry xmlns="http://purl.org/atom/ns#" xmlns:default="http://www.w3.org/1999/xhtml">
		<title xmlns="http://purl.org/atom/ns#">Improve Log Messages</title>
		<dc:subject>Coding</dc:subject>
		<dc:subject>Perl</dc:subject>
		<summary xmlns="http://purl.org/atom/ns#">summary</summary>
		<content xmlns="http://purl.org/atom/ns#" mode="escaped">&lt;p&gt;Many programs either contain poor or nonexistent logging, or log so much that any useful messages drown in the noise. This post concerns improving logs generated by scripts running on Unix, which usually suffer from the poor or nonexistent logging. Witness the succinct &lt;tt&gt;!&lt;/tt&gt; log message from the original &lt;a href="http://www.FreeBSD.org/cgi/man.cgi?query=ed&amp;sektion=1"&gt;&lt;tt&gt;ed(1)&lt;/tt&gt;&lt;/a&gt; in contrast to Java stack trace barfs. Middle ground for usable logs can be found.&lt;/p&gt;

&lt;!-- technorati tags start --&gt;&lt;p style="text-align:right;font-size:10px;"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tag/coding" rel="tag"&gt;coding&lt;/a&gt;, &lt;a href="http://www.technorati.com/tag/Perl" rel="tag"&gt;Perl&lt;/a&gt;, &lt;a href="http://www.technorati.com/tag/Unix" rel="tag"&gt;Unix&lt;/a&gt;&lt;/p&gt;&lt;!-- technorati tags end --&gt;
&lt;br /&gt;
&lt;h3&gt;Hard to Match Messages that lack Critical Information&lt;/h3&gt;
&lt;p class="sial-block-code"&gt;# Perl to open a named file,
# or exit script with error
open my $fh, '&lt;', $filename
  or die "Couldn't open $filename\n";&lt;/p&gt;
&lt;p&gt;The above failure log exhibits two problems: the error message is difficult to match using Unix command line tools, and worse, the log omits the system error message contained in the &lt;a href="http://perldoc.perl.org/perlvar.html"&gt;special &lt;tt&gt;$!&lt;/tt&gt; variable&lt;/a&gt; (&lt;tt&gt;errno&lt;/tt&gt; in C). Improve the log by omitting the apostrophe, and including the system error message:&lt;/p&gt;
&lt;p class="sial-block-code"&gt;open my $fh, '&lt;', $filename
  or die "&lt;b&gt;could not&lt;/b&gt; open $filename&lt;b&gt;: $!&lt;/b&gt;\n";&lt;/p&gt;
&lt;p&gt;Now the error message can be grepped without special handling required for the apostrophe due to &lt;a href="http://teachmeunix.com/quoting.html"&gt;Unix shell quoting problems&lt;/a&gt;, and the reason the file cannot be opened will be included.&lt;/p&gt;
&lt;p class="sial-block-shell"&gt;&lt;i&gt;# the awkward&lt;/i&gt;
$ &lt;kbd&gt;grep '^Couldn'\''t open ' script.log&lt;/kbd&gt;

&lt;i&gt;# becomes instead&lt;/i&gt;
$ &lt;kbd&gt;grep '^could not open ' script.log&lt;/kbd&gt;&lt;/p&gt;

&lt;h3&gt;Missing Metadata&lt;/h3&gt;
&lt;p&gt;The use of a script must be considered, as the context a script is run in will change the format of the logs. If the script will run in a Unix pipe chain, include the name of the script in all logs:&lt;/p&gt;
&lt;p class="sial-block-code"&gt;open my $fh, '&lt;', $filename
  or die "&lt;b&gt;$0: &lt;/b&gt;could not open $filename: $!\n";&lt;/p&gt;
&lt;p&gt;Without this information, the error would be difficult to tie to the specific command:&lt;/p&gt;
&lt;p class="sial-block-shell"&gt;$ &lt;kbd&gt;foo-util input | bar-util | zot-util &gt; output&lt;/kbd&gt;
bar-util: Could not open /var/empty: no such file or directory
&lt;/p&gt;
&lt;p&gt;I also recommend each log contain a severity level, such as &lt;tt&gt;debug&lt;/tt&gt;, &lt;tt&gt;info&lt;/tt&gt;, &lt;tt&gt;notice&lt;/tt&gt;, &lt;tt&gt;warn&lt;/tt&gt;, &lt;tt&gt;error&lt;/tt&gt;, and &lt;tt&gt;fatal&lt;/tt&gt;. This information simplifies log analysis, and allows rules whereby all &lt;tt&gt;fatal&lt;/tt&gt; logs page someone, and so forth.&lt;/p&gt;
&lt;p class="sial-block-code"&gt;open my $fh, '&lt;', $filename
  or die "$0: &lt;b&gt;fatal: &lt;/b&gt;could not open $filename: $!\n";&lt;/p&gt;
&lt;h3&gt;Regroup Metadata&lt;/h3&gt;
&lt;p&gt;Consider moving the &lt;tt&gt;$filename&lt;/tt&gt; away from the log message, and into a metadata section. This improves log message portability, if translating to other languages, and will help future searches of the data by providing more structure to match against.&lt;/p&gt;
&lt;p class="sial-block-code"&gt;open my $fh, '&lt;', $filename
  or die "$0: fatal: could not open: "
       . "&lt;b&gt;file=$filename, errno=$!&lt;/b&gt;\n";&lt;/p&gt;
&lt;p&gt;This format suits a log handling subroutine that accepts the severity, log message, and a hash of metadata to display. The log handling subroutine can then add the program name as appropriate, direct or duplicate the logs to &lt;a href="http://www.FreeBSD.org/cgi/man.cgi?query=syslog&amp;sektion=1"&gt;&lt;tt&gt;syslog(1)&lt;/tt&gt;&lt;/a&gt;, or other needs as appropriate. Use the example subroutine as a starting point.&lt;/p&gt;
&lt;p class="sial-block-code"&gt;sub remark {
  my $priority   = shift; # string
  my $message    = shift; # string
  my $attributes = shift; # hash reference

  chomp $message;

  my $attr_str;
  if ($attributes) {
    $attr_str = join ', ',
      map {
        $attributes-&gt;{$_} ||= q{};
        "$_=$attributes-&gt;{$_}"
      } sort keys %$attributes;
  }

  print STDERR "$0: $priority: $message"
    . ( $attr_str ? ": $attr_str" : q{} )
    . "\n";
  return 1;
}&lt;/p&gt;
&lt;p&gt;Example invocation of this subroutine:&lt;/p&gt;
&lt;p class="sial-block-code"&gt;remark('debug', 'test message',
  { pid =&gt; $$, line =&gt; __LINE__ } );&lt;/p&gt;
		</content>
		<issued xmlns="http://purl.org/atom/ns#">2006-10-15T14:42:13-0700</issued>
		<link xmlns="http://purl.org/atom/ns#" type="text/html" rel="alternate" href="http://sial.org/blog/2006/10/improve_log_messages.html" title=""/>
		<id xmlns="http://purl.org/atom/ns#">92</id>
	</entry>
</feed>
