home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


 


   Show Contents   Previous Page   Next Page

Chapter 10 - C API Reference Guide, Part I / Server Core Routines
Error Logging

At server startup time, Apache reopens the standard error file descriptor to the ErrorLog file.4 If configured, each virtual server can also have its own error log. Modules can write messages to the error log in a simple way just by writing directly to standard error.

However, the simple way is less than desirable because it leaves a bare string in the error log, with no indication of the time or date that the error occurred or which module left the message. Apache's error-logging API avoids these problems by providing module writers with two functions, ap_log_rerror() and ap_log_error(), both of which write nicely formatted error messages to the error log. In addition to a timestamp and a message, optional flags allow modules to include the name and line number of the C source code file where the error occurred as well as the contents of the system errno variable.

As of Version 1.3, Apache supports the notion of a message severity level. In this scheme, which should be familiar to users of the Unix syslog system,5 each message is assigned one of eight severities that range from high (APLOG_EMERG) to low (APLOG_DEBUG). A log level setting, set by the webmaster with the configuration directive LogLevel, controls which messages actually get sent to the log file. For example, if LogLevel is set to warn, only messages with severity APLOG_WARN or higher will be written to the log file. Messages at a lower priority will be ignored. This facility allows your module to write lots and lots of debugging messages at a low severity level. During module development, you can set LogLevel to a low level in order to see the debugging messages. Later you can raise the log level so that the debugging messages are suppressed on the production server.

All logging constants and routines are declared in http_log.h:

void ap_log_error (const char *file, int line, int level, const server_rec *s, const char *fmt, ...)
void ap_log_rerror (const char *file, int line, int level, const request_rec *r, const char *fmt, ...)

ap_log_rerror() and ap_log_error() are the two main entry points for the Apache error log API. These calls have many arguments, and C programmers might want to define some macros in order to save keystrokes. A couple of examples of this technique are given at the end of this section.

The first two arguments are the filename and line number where the error occurred. Most modules will want to use the APLOG_MARK macro here. It uses the C compiler __FILE__ and __LINE__ tokens to automatically pass this information. The third argument, level, is the severity level at which to record the message. level should be selected from the list of symbolic constants given later. The severity level is actually a bit mask; by setting other bits in the mask, you can adjust other logging options, as we describe later. The fourth argument is different for the two calls. For ap_log_error(), it is the server_rec, ordinarily obtained from r->server. For ap_log_rerror(), it is the request record itself, r. Internally, the logging API uses the server record to find the error log's FILE* for writing, or it passes messages to the syslog() function if native syslog support is enabled. The fifth argument, fmt, is a sprintf()-style format string. It, and the variable number of arguments that follow it, are passed to sprintf() to generate the message written to the log file.

if (!(fh = ap_pfopen(r->pool, cfg->config_file, "r"))) {
   ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
               "Cannot open configuration file %s.", cfg->config_file);
  return HTTP_INTERNAL_SERVER_ERROR;
}

One difference between ap_log_error() and ap_log_rerror() is that the latter function can optionally write the error message to the notes table under a key named error-notes. This message can then be retrieved and displayed by ErrorDocument handlers and other error processors. The message is only written to the notes table if the message severity level is warn or higher, and there is not already an error-notes entry in the notes table. Another difference is that ap_log_error() includes the client's dotted IP address in the formatted error message.

void ap_log_reason (const char *reason, const char *fname, request_rec *r)

It is so common to encounter a system error while opening a file or performing I/O on the system that a special routine is provided in the API. ap_log_reason() takes a character string describing the problem, the name of the file that was involved in the error, and the current request record. It is also common to use this function to log unsuccessful attempts to access protected documents, since the remote hos t's name is incorporated into the error message as well.

Here's a typical example of using ap_log_reason() and the line that it writes to the log file.

ap_log_reason("Can't open index.html", r->uri, r);
[Tue Jul 21 16:30:47 1998] [error] access to /
failed for w15.yahoo.com, reason: Can't open index.html No such file
or directory

Internally, ap_log_reason() is just a frontend to the following call:

ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
           "access to %s failed for %s, reason: %s",
          file,
          ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
          reason);

The level flag passed to ap_log_error() and ap_log_rerror() should be one of the severity level constants listed below, possibly logically ORed with either of the constants APLOG_NOERRNO or APLOG_WIN32ERROR.

APLOG_NOERRNO

By default, the logging API will include the contents of the system errno variable in the message. This feature is sometimes useful, as when you log an error that results from a failed system call, and sometimes not useful at all (and may in fact lead to misleading messages since errno is not reset by successful calls). Combine the severity level with APLOG_NOERRNO to suppress the automatic inclusion of errno.

ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
            "The requested URI was %s", r->uri);

APLOG_WIN32ERROR

This constant, available on Win32 platforms only, will make Apache log the value returned by the GetLastError() system call in addition to the value of errno from the standard C library.

APLOG_EMERG

This severity level indicates that an emergency condition has occurred. It should be reserved for problems that render the server unusable.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, r->server,
            "Cannot find lock file.  Aborting.");

APLOG_ALERT

This level is intended for problems that require immediate attention.

APLOG_CRIT

This logs messages at a level intended for severe problems that require immediate attention.

APLOG_ERR

This logs the message at the error severity level, intended for use with noncritical errors that nevertheless require someone's attention.

ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
             "Could not open file", r->filename);

APLOG_WARN

The warn level is one step less severe than error and is intended for warnings that may or may not require attention.

APLOG_NOTICE

notice messages are used for normal but significant conditions.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r->server,
             "Cannot connect to master database, using backup.");

APLOG_INFO

The info severity level is used for informational messages issued for nonerror conditions.

APLOG_DEBUG

The lowest severity of all is debug, used for issuing messages during the development and debugging of a module.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r->server,
             "Filename=%s,uri=%s,mtime=%d,",
            r->filename, r->uri, r->finfo.mtime);

If the ap_log_rerror() and ap_log_error() calls are too verbose for your tastes, we recommend that you create a few preprocessor macros for frequently used combinations. For example:

#define my_error(mess) ap_log_error(APLOG_MARK,\
                                 APLOG_NOERRNO|APLOG_ERROR,\
                                 r->server, mess)
#define my_debug(mess) ap_log_error(APLOG_MARK,\
                                 APLOG_NOERRNO|APLOG_DEBUG,\
                                 r->server, mess)

Now you can log simple error messages this way:

 my_error("Can't find lock file.  Aborting.");
   Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.