1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* ident	"%Z%%M%	%I%	%E% SMI" */
28 
29 package com.sun.solaris.service.logging;
30 
31 import java.text.*;
32 import java.util.*;
33 import java.util.logging.Formatter;
34 import java.util.logging.Handler;
35 import java.util.logging.LogRecord;
36 
37 import com.sun.solaris.service.exception.SuccinctStackTraceFormatter;
38 
39 /**
40  * This handler outputs LogRecords with <code>syslog(3C)</code> to the
41  * given facility with a severity translated by Level with a fixed
42  * table.  Formatters are not used.
43  *
44  * Multiple SyslogHandlers may not be in concurrent use in one virtual
45  * machine.
46  */
47 public final class SyslogHandler extends Handler {
48 	/**
49 	 * <code>syslog(3C)</code> ident string, prepended to every
50 	 * message.
51 	 */
52 	private String ident;
53 
54 	/**
55 	 * <code>syslog</code> facility to be logged to.
56 	 */
57 	private Facility facility;
58 
59 	/**
60 	 * Records the instance of this singleton.
61 	 */
62 	private static SyslogHandler instance = null;
63 
64 	/**
65 	 * Flag whether stack traces should be output when a record's
66 	 * <code>thrown</code> field is set.  They will be formatted in
67 	 * a single line by <code>SuccinctStackTraceFormatter</code>,
68 	 * which does not include the Throwable's description, since
69 	 * it's presumably described by the log message).  Default
70 	 * <code>true</code>.
71 	 */
72 	private static boolean useStackTraces = true;
73 
74 	/**
75 	 * Default logging option value.  Sets no options.  (Corresponds
76 	 * to the <code>logopt</code> argument to openlog(3c).)
77 	 */
78 	private static final int DEF_LOGOPT = 0;
79 
80 	/**
81 	 * Flag to set whether log records should indicate the record's
82 	 * logger.  (default false)
83 	 */
84 	private boolean useLoggerName = false;
85 
86 	/**
87 	 * Flag to set whether log records should indicate the last
88 	 * component of the record's logger name, if useLoggerName isn't
89 	 * set.  (default true)
90 	 */
91 	private boolean useShortLoggerName = true;
92 
93 	static {
94 		System.loadLibrary("jsyslog");
95 	}
96 
SyslogHandler(String ident, Facility facility)97 	private SyslogHandler(String ident, Facility facility)
98 	{
99 		if (ident == null || facility == null)
100 			throw new IllegalArgumentException();
101 
102 		this.ident = ident;
103 		this.facility = facility;
104 
105 		openlog(ident, DEF_LOGOPT, facility.getNative());
106 		instance = this;
107 	}
108 
109 	/**
110 	 * Return this virtual machine's instance of SyslogHandler,
111 	 * creating one which logs with the given identity to the given
112 	 * facility if necessary, unless an instance with a different
113 	 * identity or facility is already open, in which case an
114 	 * IllegalArgumentException is thrown.
115 	 *
116 	 * @throws IllegalArgumentException if the requested identity or
117 	 * facility differs from a previously-created instance.
118 	 */
getInstance(String ident, Facility facility)119 	public static SyslogHandler getInstance(String ident,
120 	   Facility facility)
121 	{
122 		if (instance != null) {
123 			if (!instance.ident.equals(ident) ||
124 			    !instance.facility.equals(facility))
125 				throw new IllegalArgumentException();
126 			else
127 				return (instance);
128 		} else
129 			return (instance = new SyslogHandler(ident, facility));
130 	}
131 
finalize()132 	public void finalize()
133 	{
134 		try {
135 			close();
136 		} catch (Exception e) {
137 			// superclass-defined exceptions do not apply
138 		}
139 	}
140 
toString()141 	public String toString()
142 	{
143 		return ("SyslogHandler(" + ident + ", " + facility.toString() +
144 		    ")");
145 	}
146 
147 	/**
148 	 * Calls <code>syslog(3C)</code>.
149 	 */
syslog(int severity, String message)150 	private static native void syslog(int severity, String message);
151 
152 	/**
153 	 * Calls <code>openlog(3C)</code>.
154 	 */
openlog(String ident, int logopt, int facility)155 	private static native void openlog(String ident, int logopt,
156 	    int facility);
157 
158 	/**
159 	 * Calls <code>closelog(3C)</code>.
160 	 */
closelog()161 	private static native void closelog();
162 
163 	/**
164 	 * Publishes the given record with its associated Severity (or
165 	 * infers its severity with Severity.severityForLevel(), if
166 	 * another type of Level is used), if the result is non-null.
167 	 */
publish(LogRecord record)168 	public void publish(LogRecord record)
169 	{
170 		Severity severity;
171 
172 		if (record.getLevel() instanceof Severity)
173 			severity = (Severity)record.getLevel();
174 		else
175 			severity = Severity.severityForLevel(record
176 			    .getLevel());
177 
178 		if (getLevel().intValue() > severity.intValue())
179 			return;
180 
181 		/*
182 		 * If the severity is null, the message isn't meant to
183 		 * be sent to syslog.
184 		 */
185 		if (severity == null)
186 			return;
187 
188 		StringBuffer message = new StringBuffer();
189 		String loggerName = record.getLoggerName();
190 		if (useLoggerName) {
191 			if (loggerName != null) {
192 				message.append("(");
193 				message.append(record.getLoggerName());
194 				message.append(") ");
195 			}
196 		} else if (useShortLoggerName) {
197 			if (loggerName != null) {
198 				message.append("(");
199 				int lastDot = loggerName.lastIndexOf('.');
200 				if (lastDot >= 0)
201 					loggerName = loggerName.substring(
202 					    lastDot + 1);
203 				message.append(loggerName);
204 				message.append(") ");
205 			}
206 		}
207 
208 		message.append(record.getMessage());
209 
210 		/*
211 		 * If the Severity is null, it's not meant to be logged
212 		 * via syslog.
213 		 */
214 		if (record.getThrown() != null && useStackTraces == true) {
215 			/*
216 			 * Format the stack trace as one line and tack
217 			 * it onto the message.
218 			 */
219 			message.append(" ");
220 			message.append(SuccinctStackTraceFormatter
221 			    .formatWithDescription(record.getThrown(),
222 			    "with tracing information: ").toString());
223 		}
224 		syslog(severity.getNative(), message.toString());
225 	}
226 
flush()227 	public void flush()
228 	{
229 	}
230 
close()231 	public void close() throws SecurityException
232 	{
233 		if (instance != null) {
234 			closelog();
235 			instance = null;
236 		}
237 	}
238 
239 	/**
240 	 * Formatters may not be used with SyslogHandler.
241 	 *
242 	 * @throws IllegalArgumentException if the use of one is
243 	 * attempted.
244 	 */
setFormatter(Formatter formatter)245 	public void setFormatter(Formatter formatter)
246 	{
247 		throw new IllegalArgumentException();
248 	}
249 
250 	/**
251 	 * Returns the <code>syslog(3C)</code> ident string, which is
252 	 * prepended to every message.
253 	 */
getIdent()254 	public String getIdent()
255 	{
256 		return (ident);
257 	}
258 
259 	/**
260 	 * Returns the <code>syslog</code> facility to be logged to.
261 	 */
getFacility()262 	public Facility getFacility()
263 	{
264 		return (facility);
265 	}
266 
267 }
268