Linux
Event Logging For Enterprise-Class Systems
Version 1.41 (not
yet fully updated for evlog-1.5.0 release)
NOTE: For a listing of features that are not yet
available, along with a list of planned enhancements to existing features, click here.
Team Members:
Larry Kessler
(lkessler@users.sourceforge.net)
James Keniston
(kenistoj@users.sourceforge.net)
Hien Nguyen (nguyhien@users.sourceforge.net)
4
Compliance with proposed POSIX Standard 1003.25
5
Linux Event
Logging Overview
6
Linux Event Logging Detailed Description
6.1.1
Fixed portion of Event Record
6.1.2
Variable-length portion of Event Record
6.1.2.2.1 Formatting Templates
6.1.2.2.1.1
General Format of a Template
6.1.2.2.1.2
Imports and Typedefs
6.1.2.2.1.2.1 Typedef Statements
6.1.2.2.1.2.2 Import Statements
6.1.2.2.1.3.1
Header of Record Template
6.1.2.2.1.3.2
Header of Struct Template
6.1.2.2.1.4
Attribute Sections
6.1.2.2.1.4.3 Attribute Dimension or Bit-Field Width
6.1.2.2.1.4.4 Const Attribute’s Value
6.1.2.2.1.4.5 Attribute Format
6.1.2.2.1.4.6 More about Typedefs
6.1.2.2.1.4.7 Alignment of Attributes in a Record or
Struct
6.1.2.2.1.5
Template Formatting Specification
6.1.2.2.1.7
More on Importing Templates
6.1.2.2.2 Using formatting templates with
POSIX_LOG_STRING and POSIX_LOG_NODATA
6.1.2.2.3
Advantages and Disadvantages
6.2 Event Logging and Retrieval
6.2.2
evlogd Daemon and Event Buffering
6.2.2.2
Discarding duplicate events
6.2.3.1.1
Writing Events from User Space.
6.2.3.1.2
Writing Events from Kernel Space
6.2.3.2
Formatting Event Records
6.2.3.3.1
The Template Repository
6.2.3.3.2 Notes on template Use in Multithreaded
Applications
6.2.3.3.4
Template Application
6.2.3.4 Registering Facilities
6.2.3.4.1 Registering Facilities from User
space
6.2.3.4.2 Registering Facilities from
Kernel space
6.2.4.1
evlconfig - Configure logging
daemon
6.2.4.2
evlfacility - Manage Facility Registry
6.2.4.3
evlsend - Event generation command
6.2.4.4 evlview - View log events
6.2.4.5
evltc - Compile formatting
templates
6.3 Filtering of Event Records
6.4.2.1
evlnotifyd Server Design
6.4.2.1.1
Processing a New Notification Request
6.4.2.2.1
posix_log_notify_add()
6.4.2.3
Client-Server Conversations
6.4.2.3.2
Creating a Notification Request
6.4.2.3.3
Getting the Status of a Notification Request
6.4.2.3.4
Removing a Notification Request
6.4.2.4.1
Registering for event notification
6.4.2.4.2
Processing an event notification
6.4.2.5
Threads-Based Notification
6.4.4.1
evlnotify - Event Notification.
6.5.1.1
evlogmgr - Event Log Manager
6.6 Query and Filter Expression Syntax Rules
6.6.1
Extensions to the POSIX Standard
6.7 Modifications to the Linux Kernel
7.1 Appendix A
POSIX 1003.25 **DRAFT**.
7.2 Appendix B Overview of Syslog 1.3 (for Linux)
Remote
Logging of syslog messages
Log File
Management / logrotate command
7.3 Appendix C Issues and New Requirements
7.4 Appendix D siginfo.h header file
7.5 Appendix E
Date and Time Formats
7.6 Appendix F
Facility Names and Facility Code creation
This document describes an
Enterprise-class Event Logging facility for Linux. Refer to Appendix
C Issues and New Requirements for items that are not currently addressed in
the body of this document.
For the purpose of problem
tracking and problem determination, events and informational messages from
kernel subsystems and system applications are logged for later analysis.
In Linux, the most commonly used
logging facilities are printk/klog
(for logging kernel events) and syslog
(for logging non-kernel events), which is referred to collectively as sysklogd. See Appendix
B Overview of Syslog 1.3 (for Linux).
Although quite suitable for single-processor desktop or small server
applications, for multi-processor medium-to-large servers, commonly found in
medium-to-large enterprises, sysklog has several shortcomings, which include:
•
Events
are recorded as text only, and some pertinent information (for example,
facility and severity) is not recorded.
Also, events can be recorded in multiple destinations, which makes
effective post-processing of messages/events difficult.
•
Limited
user interface is available to extract, filter, and view only events of
interest.
•
A
limited set of event providers (facilities) are supported, with only 8
available for the implementation to define (LOCAL0-7).
•
Limited
Event "notification" capabilities.
•
Limited
ability to manage and limit syslog generated log file sizes and the age of the
events (logrotate, a separate RPM, does provide archiving capability based on
age or size of the entire log file, but it does not allow selective removal of
events from the log files based on user or application specified
criteria).
Given sysklogd's
limitations, and the need to provide additional capabilities for large,
multi-processor, Enterprise-class systems, the goals are:
Linux Event Logging conforms
to proposed POSIX Standard 1003.25, System API - Services for Reliable,
Available, and Serviceable Systems (SRASS).
As of March 2002, this standard has not been adopted, and the
descriptions provided within are subject to change, accordingly.
Appendix
A POSIX 1003.25 **DRAFT** contains a
link to the most recent draft of this standard. The information in Appendix A will not
be repeated in the body of this document, except when additional explanation is
needed. It is recommended that you read
and become familiar with the draft POSIX standard before reading the remainder
of this document. Through active
participation in the IEEE Working Group, the authors of this document will keep
this document consistent with latest draft of the POSIX Standard.
In summary, the
requirements and functionality specified in the latest draft of the standard:
1. A single system-wide log that is always available to
accept event records.
2. All event records consist of a fixed structure
representing attributes of the event record and a variable-length data buffer
containing the event data (text or binary).
3. API functions exist for applications to:
· write to the log
· open the log (required for reading, but not required
for writing to the log)
· seek to record(s) that match the selection
criteria.
· Register for event notification, where a real-time
signal is generated and a sigval object is passed, when an event record is
written to the log which contains values that match the specified selection
criteria.
It should be noted that there is a deviation
from the "single system-wide log" requirement specified by the POSIX
standard, which is provided in order to more closely match the current behavior
of sysklogd with respect to logging AUTHPRIV messages into a separate
"private" log file with restricted read access. See Facility
Registry for a detailed explanation.
There
are two primary interfaces to Event Logging as defined by the direction
of information flow. They are the Provider
Interface and the Consumer Interface. A
secondary interface is provided for Log
Management.
The following diagram illustrates the flow of information.
http://evlog.sourceforge.net/evl1.gif
The
Provider Interface is used by software
to report events. Providers may include
commands executed from the command line or shell script. With every event, a base set of information
must be provided to make the event data useful for analysis and acceptable for
logging.
The
Event Logging system collects additional information, such as time of
the event, combines it with the provider-supplied data, and creates an event
log entry. The Event Logging System
also:
·
Optionally
takes printk() and syslog()/vsyslog() messages, and logs them as POSIX-compliant
event records.
·
Optionally, and based upon specified
thresholds, suppresses logging of duplicate events being logged in rapid
succession, to minimize system performance degradation.
·
Optionally, and based upon specified criteria,
screens events that are written to the log.
For example, messages for debugging purposes may not be of interest to a
System Administrator.
·
Filters events that are read from the
log, based on specified criteria.
·
Notifies registered Consumers, when
events match Consumer-specified criteria.
·
Provides
a method for configuring the event buffer size
The Consumer
Interface:
· Retrieves selected event data (based on Consumer specified selection criteria) from the log.
·
Registers for notification (based on Consumer specified criteria) when
events matching the criteria are written to the log.
The Consumer-interface users
typically include system end users (mostly system administrators and operators)
and service/support groups.
Log Management:
·
Provides
methods for:
o
Managing the size of the event log.
o
Specifying
criteria for automatically removing events from the log that are no longer of
interest, truncating the log, and reclaiming the space.
An
Event Record consists of a fixed-length, predefined structure, and a variable-length
portion, up to POSIX_LOG_ENTRY_MAXLEN long, which contains event data
attributes that are passed by the provider of the event record.
See Appendix A for contents
of the fixed-portion of the event record.
Additional content has been defined as follows:
·
The following log_event_types are reserved:
EVL_PRINTK_MESSAGE (0x2) - indicates that event was originally
written with the printk() function. All messages written with printk() will have
this event type unless the event logging feature that includes caller location
information (source file, function, line number) is enabled, in which case the event type will
be a checksum derived from source file name, function name, and printk format
string. The log_facility will always be LOG_KERN for this event type.
EVL_SYSLOG_MESSAGE (0x1) - indicates that event was originally
written with the syslog() or vsyslog()
function. The log_facility will be passed by the caller of syslog().
EVL_BUFFER_OVERRUN
(0x6) - indicates that one or more event records have been discarded due to an
overrun of the kernel event buffer, or temporary event buffer allocated by the
evlogd daemon. The log_facility will always be LOG_LOGMGMT for this event type. See Event
Buffer and evlogd Daemon for more information.
EVL_DUPS_DISCARDED
(0x7) - indicates that one or more duplicate event records have been
discarded. The log_facility will always be LOG_LOGMGMT for this event type. See Discarding
duplicate events for more information.
EVLOG_REGISTER_FAC
(40) - indicates that the event is a special event generated when
evl_register_facility() is called in the kernel. See Registering
Facilities from Kernel space for details.
·
The following log_flags bits have been reserved for use by event logging:
POSIX_LOG_TRUNCATE
(0x1) - Defined in the POSIX standard.
EVL_KERNEL_EVENT
(0x2) - Indicates that event was logged from within the kernel. If caller of log write functions in
user-space (evl_log_write, posix_log_write, etc.) sets this flag to 1,
ECANCELED is returned and the event is not logged. This is intended to prevent an application or
user from erroneously indicating that the event was logged in the kernel, when
in fact it was logged in user-space.
EVL_INITIAL_BOOT_EVENT
(0x4) - This bit indicates that the event was logged before a valid time value was available in the kernel. When this bit is set the evlogd daemon will overwrite the log_time field in the event record with the system startup
time (determined by subtracting uptime from current time), and thus provides a
reasonable approximation for log_time.
EVL_KERNTIME_LOCAL
(0x8) - This bit indicates that the log_time value in the event record (created in the kernel) is set to local
time, and needs to be adjusted to GMT by the evlogd
daemon. evlogd will reset this bit
after the time is adjusted.
EVL_INTERRUPT
(0x10) - This bit indicates that the event was logged from interrupt
context.
EVL_PRINTK_MESSAGE (0x20) - indicates that event was originally
written with the printk() function.
Reserved
for future event logging use: 0x40,
0x80.
The
2 variable-length data formats defined
in the POSIX standard are supported, as indicated by the log_format value:
·
POSIX_LOG_STRING
- A NULL-terminated string.
·
POSIX_LOG_BINARY
- Binary data, requiring additional information to interpret and format.
POSIX_LOG_NODATA is also a valid log_format, and indicates that there is no variable-length data associated with the event.
The
POSIX standard requires that compliant implementations support variable-length
event data consisting of a NULL-terminated character string.
Binary
data can be described as a contiguous string of bytes representing one or more
attributes in the optional portion of an event record. The information needed for parsing and
formatting the optional attributes contained within this binary data is
provided by formatting templates.
NOTE: Although formatting templates are primarily intended for use with log_format of POSIX_LOG_BINARY, they can also be used with other log_formats. See 6.1.2.2.2 Using formatting templates with POSIX_LOG_STRING and POSIX_LOG_NODATA for details.
There are 5 sections
in a template:
These sections are described in detail below.
The
imports/typedefs, header, and attribute sections of a template are made up of
statements. In general, statement syntax
follows the conventions of the C language.
In particular, the following language elements follow C syntax:
· comments
· identifiers
· string and wide-string literals, including automatic
concatenation of adjacent string literals or adjacent wide-string literals
· integer, char, wide-char, and floating-point
constants
· white space
In
the header and attribute sections, statements are terminated with semicolons (;). A single line can contain
multiple statements, and a single statement can span multiple lines.
The
final section of the template, the template formatting specification, may be in
the form of a statement, or it may be free-form text. Its format is defined later in this
discussion.
The
special statement END (upper case, no semicolon, no white space, no comments,
on a line by itself) is used to separate multiple templates in the same source
file. The final template in a file need
not contain an END statement.
The
evltc template compiler has the option of passing your template source code
through the C preprocessor, cpp, before compiling it. Therefore, your template source code can
include C-preprocessor directives such as #include, #define, and #ifdef, with
appropriate results.
The imports/typedefs section contains zero or more import statements and zero or more typedef statements. Within this section, import and typedef statements can appear intermixed in any order. The imports and typedefs remain in effect through the end of the template source file.
6.1.2.2.1.2.1 Typedef Statements
A typedef statement in a template is analogous to a typedef statement in the C language. Each typedef statement has the form
typedef type_spec name
dimension_spec format_spec ;
where type_spec, name, dimension_spec, and format_spec are defined as for attribute statements. (See “Attribute Sections” later in this discussion.) The specified name can be used as a type_spec in subsequent attribute statements. Each such attribute assumes the typedef’s type, its dimension (if any), and possibly its format (if any). For a further explanation of typedefs, see the “More on Typedefs” section under “Attribute Sections.”
6.1.2.2.1.2.2 Import Statements
Each import statement has the form
import struct_path ;
A struct_path is a sequence of one or more identifiers delimited by periods. This sequence identifies a struct template (see next section) that is referenced later in this template file. For example, the statement
import gui.graphics.point;
would typically import the struct template from the file /var/evlog/templates/gui/graphics/point.to. The importing template can then declare attributes of type “struct point” or “struct gui.graphics.point”.
The final component of a struct_path can be an asterisk (*), specifying that all struct templates from the indicated directory should be imported. For example, the statement
import gui.graphics.*;
would typically import the templates from all the .to files in the /var/evlog/templates/gui/graphics directory.
Importing a struct template brings a binary representation of that template into memory, much as if that template had been previously defined in the current template source file. Importing of templates is further discussed in the section “More on Importing Templates,” later in this discussion of formatting templates.
There are two types of templates that are used to describe event records:
Record and struct templates differ primarily in the content of their header sections.
6.1.2.2.1.3.1 Header of Record Template
The header of a record template contains 3 statements that must be specified in the following order. (The description statement is optional.)
facility
facility_id ;
event_type
event_id ;
description
description ;
The facility_id identifies the facility to which this template applies; it corresponds to the log_facility member in the fixed portion of an event record. The facility_id is either an integer constant or a string literal from the facility registry (see Facility Registry) -- e.g.,
facility 128;
facility "MAIL";
facility "Bob’s Volume Manager";
The event_id is an integer constant that corresponds to an event record's log_event_type member. Together, the facility and event-type match the template to a particular type of event record.
An event_type statement may also be of the form
event_type default;
This indicates that this template will be used by default for any event type, in the indicated facility, that does not have its own template.
The description is a string literal that contains a human-readable title for the template.
6.1.2.2.1.3.2 Header of Struct Template
The header of a struct template contains a struct statement and an optional description statement, in that order. The description statement is as described above. The format of a struct statement is
[const] struct struct_name ;
where struct_name is an identifier that uniquely identifies this struct.
A struct template that is declared const must have a const-attributes section and no record-attributes section. Such a template is used solely to define const values that may be referenced in other templates. A non-const struct template must have at least a record-attributes section.
A
template may have a record-attributes section and/or a const-attributes
section. The const-attributes section
defines attributes with fixed values that are provided by the template, and are
not included in the event record. The
record-attributes section defines attributes in the order that they appear in
the optional part of the event record.
Each
section consists of three parts:
1. the start-attributes clause. For the const-attributes section, this is “const {“. For the
record-attributes section, this is “[aligned] attributes {“. The optional “aligned” keyword specifies that
the attributes are aligned as if they were members of a C struct (and therefore
may contain padding after some values).
The “aligned” option is discussed further under “Alignment of Attributes
in a Record or Struct.”
2. one or more attribute statements, each of which
resembles a C-language data declaration
3. the end-attributes clause: “}”
In the record-attributes section, an attribute statement specifies the
name, type, and formatting specification of an attribute, and (if it is an
array or bit-field) its dimension. The
format of the statement is
type_spec name dimension_spec format_spec ;
In
the const-attributes section, each attribute statement also specifies a value
for the attribute (unless the attribute is a const struct, which carries its
own predefined values):
type_spec name dimension_spec = value_spec format_spec ;
The
record-attributes section cannot include attributes that are const structs.
6.1.2.2.1.4.1 Attribute Name
The attribute’s name is a valid C-language identifier that distinguishes this attribute from other attributes defined in this template. (However, this name may be the same as the name of one or more attributes defined in struct templates referenced by this template.) The following attribute names are reserved, as explained in POSIX 1003.25: recid, size, format, event_type, facility, severity, uid, gid, pid, pgrp, time, flags, thread, processor, data. The following words are also reserved: aligned, attributes, const, default, description, import, signed, struct, typedef, unsigned, and all the type names listed in the “Table of Optional Attribute Data Types and Format Specifiers” later in this section.
6.1.2.2.1.4.2 Attribute Type
The attribute’s type_spec can take one of the following forms:
· one of the type names listed in the table later in this section (e.g., “ushort”, “ldouble”);
· the C-language equivalent of one of those type names (e.g., “unsigned short”, “long double”);
· the name of a previously defined typedef (see “More about Typedefs” later in this section); or
· the construct “struct struct_name” where struct_name is the name of a struct template that was previously defined or imported in the current template source file. If the specified struct template was not previously defined or imported in the current file, but a file named struct_name.to exists in the same directory as the current file, that struct template will be imported automatically.
6.1.2.2.1.4.3 Attribute Dimension or Bit-Field Width
The attribute’s dimension_spec is omitted if the attribute is not an array or bit-field. If the attribute is an array, the dimension_spec is one of the following constructs, enclosed in square brackets [ ]:
A
const attribute cannot have a dimension of [_R_] or [*].
Multi-dimensional
attributes per se are not supported.
However, you could obtain the effect of a two-dimensional array as
follows: A struct may contain an array as its only attribute, and another
template may declare an array of such structs.
Bit-Fields.
If the attribute is a bit-field, the dimension_spec has the format
: width
where
width is the attribute’s size in bits,
an integer constant. Bit-field
attributes have the same semantics as C-language bit-fields, with the following
exception:
· The width must
be a decimal, hexadecimal, or octal integer constant.
Bit-field
storage and alignment conventions match those of gcc on the system that wrote
the event log. The size of a bit-field
cannot exceed the size of a long long.
Unnamed bit-fields are permitted.
In particular, the “int :0” construct (which
in C means, “skip to the beginning of the next storage unit”) is supported.
A
const attribute cannot be a bit-field.
6.1.2.2.1.4.4 Const Attribute’s Value
A
const attribute’s value_spec is a
constant (string literal, or integer, floating-point, or character constant)
that specifies the attribute’s value.
For aggregate attributes (records and arrays), C-style aggregate
initializers are used – for example:
const {
struct point origin = {0,0};
int daysPerMonth[12] = {31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31} “(%d )”;
struct xpg4Msg probableCauses[] = {
{2, 1, “Operator fell asleep”},
{2, 2, “Backhoe”}
} “(Probable cause[%I] = %Z\n)”;
}
Formatting
of arrays and structs is discussed in the next subsection.
Due
to automatic concatenation of string literals, the following attribute
statement, though legal, will probably not produce the intended effect:
string helpMsg = “Help!” “%-20s”;
To
resolve this problem, even a scalar attribute’s value_spec can be enclosed in curly braces – e.g.,
string helpMsg = { “Help!” } “%-20s”;
6.1.2.2.1.4.5 Attribute Format
The attribute’s format_spec is a string literal that specifies how the attribute is to be formatted.
If the attribute is a scalar (simple) attribute – not an array or struct – the format_spec must be a valid printf format specification for a single value of that type, beginning with a “%” and ending with one of the format specifiers from the table below. The standard printf flags (‘-‘, ‘+’, space, ‘#’, and ‘0’) and field-width and precision specifiers are also permitted.
If the format_spec is omitted, the format specifier defaults to the one shown for that data type in the table. For example, the default format_spec for ints, shorts, and chars is “%d”.
The special format specifiers %t, %b, and %v are also supported, as described later in this section.
Table of
Optional Attribute Data Types and Format Specifiers used with formatting
templates.
|
Type Names |
Applicable printf Format Specifiers |
Default Specifier |
|
short |
d, i, o, x, X, u without flag h or l |
d |
|
ushort |
d , i, o, x, X, u without flag h or l |
u |
|
int |
d , i, o, x, X, u without flag h or l |
d |
|
uint |
d , i, o, x, X, u without flag h or l |
u |
|
long |
d , i, o, x, X, u with flag l |
ld |
|
ulong |
d , i, o, x, X, u with flag l |
lu |
|
longlong |
d , i, o, x, X, u with flag L |
lld |
|
ulonglong |
d , i, o, x, X, u with flag L |
llu |
|
address |
d, i, o, x, X, u with flag l, p |
|
|
float |
f, e, E, g, G |
f |
|
double |
f, e, E, g, G |
f |
|
ldouble |
flag L with f, e, E, g, G |
Lf |
|
schar |
c, C, d , i, o, x, X, u without flag h or l |
d |
|
uchar |
c, C, u , i, o, x, X, u without flag h or l |
u |
|
char |
Same as either schar (signed char) or uchar, depending on whether char is signed or unsigned on the system where evltc was built. |
d or u |
|
string |
s |
s |
|
wchar |
C, flag l with c |
lc |
|
wstring |
S, flag l with s |
ls |
Dump Format. The format_spec can be %t for any attribute. %t indicates that the data should be displayed in dump format: hexadecimal, with the ASCII equivalent also displayed. For example, given an attribute definition of “uchar x[32]”, a formatting specifier of %t might produce the following:
00000000
61 62 63 64 65 66 67 68 61 62 63 64 65
66 67 68 | abcdefgh abcdefgh
00000010 3F 3F 3F 3F 4A 3F 3F 3F 3F 3F 3F 3F 4A 3F 3F 3F | ????J??? ????J???
Bitmaps. For integer types, the %b format specifier can be used. %b specifies that the attribute should be formatted as a bitmap. A %b specifier has the following format:
"%b/bit_pattern1/string1/bit_pattern2/string2/.../bit_patternN/stringN/"
Each bit pattern is either a hexadecimal number beginning with 0x or 0X or x or X, or a binary pattern beginning with 0b or 0B or b or B. For every bit pattern that matches the attribute’s value, the corresponding string is included in the formatted representation of the value.
In the examples shown in this section, the strings and
bit patterns are delimited by slashes (/), but any punctuation character may be
used as a delimiter. In a %b or %v
format specifier, the delimiter is the character immediately following the %b
or %v. The delimiter character cannot
appear in any of the strings.
A hexadecimal bit pattern matches the attribute’s value if all the 1s in the hex value are also 1s in the attribute’s value – i.e., (attribute_value & hex_pattern) == hex_pattern. For example, when the format specifier
"%b/0x1/HUMAN/0x2/ADULT/0x4/FEMALE/"
is applied to the value 5, the result is
0x5(HUMAN|FEMALE)
If it is applied to the value 8, the result is
0x8
A binary pattern is a sequence of 0s, 1s, and Xs (upper or lower case), each representing a bit. All X bits, and all bits not explicitly specified, are treated as don’t-care bits. 0 and 1 bits must match the attribute’s value. For example, the previous sample specification could be expressed as
"%b/0b1/HUMAN/0b1x/ADULT/0b1xx/FEMALE/"
To give non-humans, juveniles, and males (the “zero” bits) explicit recognition, we could expand the specification to
"%b/0b1/HUMAN/0b1x/ADULT/0b1xx/FEMALE/"
"0b0/INHUMAN/0b0x/JUVENILE/0b0xx/MALE/"
(Note that the two adjacent strings are automatically concatenated.) Applied to the value 5, this specifier would produce
0x5(HUMAN|FEMALE|JUVENILE)
Once a pattern is matched, any subsequent patterns associated with those bits are ignored. For example, If the specification
"%b/0x7/WOMAN/0x1/HUMAN/0x2/ADULT/0x4/FEMALE/"
is applied to the value 7, the result is
0x7(WOMAN)
not
0x7(WOMAN|HUMAN|ADULT|FEMALE)
Named Values. Like %b, the %v format specifier is restricted to integer types, and specifies a set of value/string pairs. When a value in the %v list exactly matches the attribute’s value, the corresponding string is substituted. If there is no match, the attribute’s value in decimal is substituted. For example, when the format specifier
"%v/1/Sun/2/Mon/3/Tue/4/Wed/5/Thu/6/Fri/7/Sat/"
is
applied to the value 6, the result is “Fri”.
When it is applied to the value 8, the result is “8”.
Struct Attributes. If the attribute is a struct, the format_spec must be omitted, since the format is specified in the struct’s template. The special %Z specifier is used when formatting arrays of structs, as described under “Array Attributes.”
Array Attributes. If the attribute is an array, the format_spec (if any) may be followed by the construct
delimiter=string
where string is a string literal. This delimiter string is inserted between the
elements of the formatted array. The
default delimiter is a single space.
If the format_spec is omitted, the array's elements will be formatted using the default format for that type (or %Z, for an array of structs).
If specified, the format_spec must be one of the following constructs:
int widgets[5] "%#x";
might yield the following formatted output:
0x5 0xa 0xc 0x43 0x3
Within the format_spec, %I and %Z have special interpretations. %I is replaced by the index (in decimal) of the current array element. For example, the attribute statement
int widgets[5] "w[%I]=%2d" delimiter="\n";
might yield the following formatted output:
w[0]= 5
w[1]=10
w[2]=12
w[3]=67
w[4]= 3
%Z is used with arrays of structs, and
stands for a single element of the array, formatted according to the struct’s
template. For example, the attribute
statement
struct point endPoints[2] "endPoint[%I] = %Z" delimiter="\n";
might yield the following output:
endPoint[0] = (10,15)
endPoint[1] = (-25,15)
int widgets[5] "(%d, )";
might yield the following formatted output:
5, 10, 12, 67, 3,
6.1.2.2.1.4.6 More about Typedefs
The
semantics of the components of a typedef statement are largely the same as
those of an attribute statement. A
typespec defines a type, and may also define a dimension and/or format. When a typedef’s name is given as an
attribute’s type_spec, the attribute is
defined as follows:
· The typedef’s type is assigned to the attribute.
· If the typedef defines a dimension, that will be
assigned to the attribute; otherwise, the attribute’s dimension (if specified)
will be used. It is illegal for both the
typedef and the attribute to explicitly specify dimensions.
· If the attribute statement does not explicitly
specify a format, the typedef’s format (if any) will be assigned to the
attribute.
A
typedef is not associated with any particular template. Therefore, its dimension cannot be an
attribute name. However, it can be
[_R_].
A
typedef with a dimension of [ ] -- for example,
typedef int int_array_t[] “(%d )”;
--
is legal. However, only const attributes
can be of this type (since the size of the array is determined from the
initializer list).
A
typedef cannot define a bit-field. A
typedef cannot be initialized with a value.
A
typedef name cannot be a reserved word.
However, the same (legal) name can be used independently for a typedef,
struct, and/or attribute.
The
form
typedef struct s { ... } s_t;
is
allowed in C but not in a template. The
equivalent code for a template is:
struct s; [aligned] attributes { ... } format string “...”
END
typedef struct s s_t;
6.1.2.2.1.4.7 Alignment of Attributes in a Record or Struct
The “aligned” keyword in a record-attributes clause specifies that, in the event record, the attributes’ values will be aligned as if they were members of a C struct. As a result, the record is expected to contain padding between values (and possibly after the last value) to achieve alignment.
For example, a struct defined as follows --
attributes { char c; int i; }
-- is expected to occupy 5 bytes on a 32-bit system: 1 for c and 4 for i. The record might be written as follows:
evl_log_write(facility, event_type, severity, flags,
"char", c, "int", i);
By contrast, a struct defined as follows --
aligned attributes { char c; int i; }
-- is expected to occupy 8 bytes on a 32-bit system: 1 for c, 3 for alignment, and 4 for i. The record might be written as follows:
struct s { char c; int i; } svar;
...
posix_log_write(facility,
event_type, severity, &svar,
sizeof(svar), POSIX_LOG_BINARY, flags);
In a record
or struct with aligned attributes, no attribute can have a size that is unknown
at compile time. Such attributes include
strings, wstrings, variable-dimension arrays, and unaligned structs.
The
last section of the template defines how the record’s formatted attributes are
to be presented. This section must
appear immediately after the final attribute section. This section can take three forms:
· free form
· format string string_literal
· format wstring wide_string_literal
A
free-form formatting specification begins with the keyword “format” on a line
by itself. Everything that follows the
format statement, until the end of the template, is interpreted as the format
string.
The formatted representation of a template is a copy of this format string, with the appropriate formatted attribute value substituted wherever an %attribute_name% construct appears. attribute_name can be a simple name (e.g., “employee”) or a reference to a struct member (e.g., “employee.addr.city”).
The %attribute_name% construct may include an embedded formatting specification -- e.g. "%lun:x% -- that overrides the format that would otherwise be used. Any newline (line break) in the format string also appears in the formatted output, unless it is immediately preceded by a \. Newlines, tabs, and other special characters can also be encoded using standard C escapes (\n, \t, etc.).
For
an event-record template, a newline is automatically appended to the formatted
representation, so that the formatted output of an event-record template always
takes up at least one complete line. For
a struct template, any terminating newline must be explicitly specified – as a
trailing blank line, as a \n escape, or as the last character of a formatted
array attribute.
The
C preprocessor does not always preserve white space intact. So if you are going to run your template
source file through the C preprocessor, you may want to use the “format string”
or “format wstring” form instead.
This
form consists of the words “format string”, followed by one or more string
literals, which are automatically concatenated into the format string. The words “format” and “string” must be
separated by at least one white-space character (but no newlines).
This
form consists of the words “format wstring”, followed by one or more
wide-string literals, which are automatically concatenated.
Here
is a simple struct template:
struct point;
attributes {
int x; /* Format defaults to %d */
int y;
}
format string "(%x%,%y%)"