001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.loggers;
028 import org.opends.messages.Message;
029
030 import java.io.File;
031 import java.io.IOException;
032 import java.util.*;
033
034 import org.opends.server.api.*;
035 import org.opends.server.core.DirectoryServer;
036 import org.opends.server.config.ConfigException;
037 import org.opends.server.types.*;
038
039 import static org.opends.messages.ConfigMessages.*;
040 import static org.opends.messages.LoggerMessages.*;
041 import org.opends.messages.Severity;
042 import org.opends.messages.Category;
043 import org.opends.server.admin.std.server.ErrorLogPublisherCfg;
044 import org.opends.server.admin.std.server.FileBasedErrorLogPublisherCfg;
045 import org.opends.server.admin.std.meta.ErrorLogPublisherCfgDefn;
046 import org.opends.server.admin.server.ConfigurationChangeListener;
047 import static org.opends.server.util.StaticUtils.getFileForPath;
048 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
049 import org.opends.server.util.TimeThread;
050 import static org.opends.server.util.ServerConstants.*;
051
052
053 /**
054 * This class provides an implementation of an error log publisher.
055 */
056 public class TextErrorLogPublisher
057 extends ErrorLogPublisher<FileBasedErrorLogPublisherCfg>
058 implements ConfigurationChangeListener<FileBasedErrorLogPublisherCfg>
059 {
060 private TextWriter writer;
061
062 private FileBasedErrorLogPublisherCfg currentConfig;
063
064 /**
065 * Returns an instance of the text error log publisher that will print
066 * all messages to the provided writer. This is used to print the messages
067 * to the console when the server starts up.
068 *
069 * @param writer The text writer where the message will be written to.
070 * @return The instance of the text error log publisher that will print
071 * all messages to standard out.
072 */
073 public static TextErrorLogPublisher
074 getStartupTextErrorPublisher(TextWriter writer)
075 {
076 TextErrorLogPublisher startupPublisher = new TextErrorLogPublisher();
077 startupPublisher.writer = writer;
078
079 startupPublisher.defaultSeverities.addAll(Arrays.asList(Severity.values()));
080
081 return startupPublisher;
082 }
083
084 /**
085 * {@inheritDoc}
086 */
087 public void initializeErrorLogPublisher(FileBasedErrorLogPublisherCfg config)
088 throws ConfigException, InitializationException
089 {
090 File logFile = getFileForPath(config.getLogFile());
091 FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
092
093 try
094 {
095 FilePermission perm =
096 FilePermission.decodeUNIXMode(config.getLogFilePermissions());
097
098 LogPublisherErrorHandler errorHandler =
099 new LogPublisherErrorHandler(config.dn());
100
101 boolean writerAutoFlush =
102 config.isAutoFlush() && !config.isAsynchronous();
103
104 MultifileTextWriter writer =
105 new MultifileTextWriter("Multifile Text Writer for " +
106 config.dn().toNormalizedString(),
107 config.getTimeInterval(),
108 fnPolicy,
109 perm,
110 errorHandler,
111 "UTF-8",
112 writerAutoFlush,
113 config.isAppend(),
114 (int)config.getBufferSize());
115
116 // Validate retention and rotation policies.
117 for(DN dn : config.getRotationPolicyDNs())
118 {
119 writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
120 }
121
122 for(DN dn: config.getRetentionPolicyDNs())
123 {
124 writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
125 }
126
127 if(config.isAsynchronous())
128 {
129 this.writer = new AsyncronousTextWriter("Asyncronous Text Writer for " +
130 config.dn().toNormalizedString(), config.getQueueSize(),
131 config.isAutoFlush(),
132 writer);
133 }
134 else
135 {
136 this.writer = writer;
137 }
138 }
139 catch(DirectoryException e)
140 {
141 Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(
142 config.dn().toString(), String.valueOf(e));
143 throw new InitializationException(message, e);
144
145 }
146 catch(IOException e)
147 {
148 Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(
149 logFile.toString(), config.dn().toString(), String.valueOf(e));
150 throw new InitializationException(message, e);
151
152 }
153
154 Set<ErrorLogPublisherCfgDefn.DefaultSeverity> defSevs =
155 config.getDefaultSeverity();
156 if(defSevs.isEmpty())
157 {
158 defaultSeverities.add(Severity.FATAL_ERROR);
159 defaultSeverities.add(Severity.SEVERE_ERROR);
160 defaultSeverities.add(Severity.SEVERE_WARNING);
161 } else
162 {
163 for(ErrorLogPublisherCfgDefn.DefaultSeverity defSev : defSevs)
164 {
165 if(defSev.toString().equalsIgnoreCase(LOG_SEVERITY_ALL))
166 {
167 defaultSeverities.add(Severity.FATAL_ERROR);
168 defaultSeverities.add(Severity.INFORMATION);
169 defaultSeverities.add(Severity.MILD_ERROR);
170 defaultSeverities.add(Severity.MILD_WARNING);
171 defaultSeverities.add(Severity.NOTICE);
172 defaultSeverities.add(Severity.SEVERE_ERROR);
173 defaultSeverities.add(Severity.SEVERE_WARNING);
174 }
175 else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE))
176 {
177 // don't add any severity
178 }
179 else
180 {
181 Severity errorSeverity =
182 Severity.parseString(defSev.name());
183 if(errorSeverity != null)
184 {
185 defaultSeverities.add(errorSeverity);
186 }
187 }
188 }
189 }
190
191 for(String overrideSeverity : config.getOverrideSeverity())
192 {
193 if(overrideSeverity != null)
194 {
195 int equalPos = overrideSeverity.indexOf('=');
196 if (equalPos < 0)
197 {
198 Message msg =
199 WARN_ERROR_LOGGER_INVALID_OVERRIDE_SEVERITY.get(overrideSeverity);
200 throw new ConfigException(msg);
201
202 } else
203 {
204 String categoryName = overrideSeverity.substring(0, equalPos);
205 categoryName = categoryName.replace("-", "_").toUpperCase();
206 try
207 {
208 Category category = Category.valueOf(categoryName);
209
210 HashSet<Severity> severities =
211 new HashSet<Severity>();
212 StringTokenizer sevTokenizer =
213 new StringTokenizer(overrideSeverity.substring(equalPos+1), ",");
214 while (sevTokenizer.hasMoreElements())
215 {
216 String severityName = sevTokenizer.nextToken();
217 severityName = severityName.replace("-", "_").toUpperCase();
218 if(severityName.equalsIgnoreCase(LOG_SEVERITY_ALL))
219 {
220 severities.add(Severity.FATAL_ERROR);
221 severities.add(Severity.INFORMATION);
222 severities.add(Severity.MILD_ERROR);
223 severities.add(Severity.MILD_WARNING);
224 severities.add(Severity.NOTICE);
225 severities.add(Severity.SEVERE_ERROR);
226 severities.add(Severity.SEVERE_WARNING);
227 }
228 else
229 {
230 try
231 {
232 Severity severity =
233 Severity.parseString(severityName);
234
235 severities.add(severity);
236 }
237 catch(Exception e)
238 {
239 Message msg =
240 WARN_ERROR_LOGGER_INVALID_SEVERITY.get(severityName);
241 throw new ConfigException(msg);
242 }
243 }
244 }
245 definedSeverities.put(category, severities);
246 }
247 catch(Exception e)
248 {
249 Message msg = WARN_ERROR_LOGGER_INVALID_CATEGORY.get(categoryName);
250 throw new ConfigException(msg);
251 }
252 }
253 }
254 }
255
256 currentConfig = config;
257
258 config.addFileBasedErrorChangeListener(this);
259 }
260
261
262
263 /**
264 * {@inheritDoc}
265 */
266 @Override()
267 public boolean isConfigurationAcceptable(ErrorLogPublisherCfg configuration,
268 List<Message> unacceptableReasons)
269 {
270 FileBasedErrorLogPublisherCfg config =
271 (FileBasedErrorLogPublisherCfg) configuration;
272
273 return isConfigurationChangeAcceptable(config, unacceptableReasons);
274 }
275
276 /**
277 * {@inheritDoc}
278 */
279 public boolean isConfigurationChangeAcceptable(
280 FileBasedErrorLogPublisherCfg config, List<Message> unacceptableReasons)
281 {
282 // Make sure the permission is valid.
283 try
284 {
285 FilePermission filePerm =
286 FilePermission.decodeUNIXMode(config.getLogFilePermissions());
287 if(!filePerm.isOwnerWritable())
288 {
289 Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(
290 config.getLogFilePermissions());
291 unacceptableReasons.add(message);
292 return false;
293 }
294 }
295 catch(DirectoryException e)
296 {
297 Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(
298 config.getLogFilePermissions(), String.valueOf(e));
299 unacceptableReasons.add(message);
300 return false;
301 }
302
303 for(String overrideSeverity : config.getOverrideSeverity())
304 {
305 if(overrideSeverity != null)
306 {
307 int equalPos = overrideSeverity.indexOf('=');
308 if (equalPos < 0)
309 {
310 Message msg = WARN_ERROR_LOGGER_INVALID_OVERRIDE_SEVERITY.get(
311 overrideSeverity);
312 unacceptableReasons.add(msg);
313 return false;
314
315 } else
316 {
317 String categoryName = overrideSeverity.substring(0, equalPos);
318 categoryName = categoryName.replace("-", "_").toUpperCase();
319 try
320 {
321 Category.valueOf(categoryName);
322 }
323 catch(Exception e)
324 {
325 Message msg = WARN_ERROR_LOGGER_INVALID_CATEGORY.get(categoryName);
326 unacceptableReasons.add(msg);
327 }
328
329 StringTokenizer sevTokenizer =
330 new StringTokenizer(overrideSeverity.substring(equalPos+1), ",");
331 while (sevTokenizer.hasMoreElements())
332 {
333 String severityName = sevTokenizer.nextToken();
334 severityName = severityName.replace("-", "_").toUpperCase();
335 if(!severityName.equalsIgnoreCase(LOG_SEVERITY_ALL))
336 {
337 try
338 {
339 Severity.parseString(severityName);
340 }
341 catch(Exception e)
342 {
343 Message msg =
344 WARN_ERROR_LOGGER_INVALID_SEVERITY.get(severityName);
345 unacceptableReasons.add(msg);
346 return false;
347 }
348 }
349 }
350 }
351 }
352 }
353 return true;
354 }
355
356 /**
357 * {@inheritDoc}
358 */
359 public ConfigChangeResult applyConfigurationChange(
360 FileBasedErrorLogPublisherCfg config)
361 {
362 // Default result code.
363 ResultCode resultCode = ResultCode.SUCCESS;
364 boolean adminActionRequired = false;
365 ArrayList<Message> messages = new ArrayList<Message>();
366
367 Set<ErrorLogPublisherCfgDefn.DefaultSeverity> defSevs =
368 config.getDefaultSeverity();
369 defaultSeverities.clear();
370 if(defSevs.isEmpty())
371 {
372 defaultSeverities.add(Severity.FATAL_ERROR);
373 defaultSeverities.add(Severity.SEVERE_ERROR);
374 defaultSeverities.add(Severity.SEVERE_WARNING);
375 } else
376 {
377 for(ErrorLogPublisherCfgDefn.DefaultSeverity defSev : defSevs)
378 {
379 if(defSev.toString().equalsIgnoreCase(LOG_SEVERITY_ALL))
380 {
381 defaultSeverities.add(Severity.FATAL_ERROR);
382 defaultSeverities.add(Severity.INFORMATION);
383 defaultSeverities.add(Severity.MILD_ERROR);
384 defaultSeverities.add(Severity.MILD_WARNING);
385 defaultSeverities.add(Severity.NOTICE);
386 defaultSeverities.add(Severity.SEVERE_ERROR);
387 defaultSeverities.add(Severity.SEVERE_WARNING);
388 }
389 else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE))
390 {
391 // don't add any severity
392 }
393 else
394 {
395 Severity errorSeverity =
396 Severity.parseString(defSev.name());
397 if(errorSeverity != null)
398 {
399 defaultSeverities.add(errorSeverity);
400 }
401 }
402 }
403 }
404
405 definedSeverities.clear();
406 for(String overrideSeverity : config.getOverrideSeverity())
407 {
408 if(overrideSeverity != null)
409 {
410 int equalPos = overrideSeverity.indexOf('=');
411 if (equalPos < 0)
412 {
413 Message msg = WARN_ERROR_LOGGER_INVALID_OVERRIDE_SEVERITY.get(
414 overrideSeverity);
415 resultCode = DirectoryServer.getServerErrorResultCode();
416 messages.add(msg);
417 } else
418 {
419 String categoryName = overrideSeverity.substring(0, equalPos);
420 categoryName = categoryName.replace("-", "_").toUpperCase();
421 try
422 {
423 Category category = Category.valueOf(categoryName);
424
425 HashSet<Severity> severities =
426 new HashSet<Severity>();
427 StringTokenizer sevTokenizer =
428 new StringTokenizer(overrideSeverity.substring(equalPos+1), ",");
429 while (sevTokenizer.hasMoreElements())
430 {
431 String severityName = sevTokenizer.nextToken();
432 severityName = severityName.replace("-", "_").toUpperCase();
433 if(severityName.equalsIgnoreCase(LOG_SEVERITY_ALL))
434 {
435 severities.add(Severity.FATAL_ERROR);
436 severities.add(Severity.INFORMATION);
437 severities.add(Severity.MILD_ERROR);
438 severities.add(Severity.MILD_WARNING);
439 severities.add(Severity.NOTICE);
440 severities.add(Severity.SEVERE_ERROR);
441 severities.add(Severity.SEVERE_WARNING);
442 }
443 else
444 {
445 try
446 {
447 Severity severity =
448 Severity.parseString(severityName);
449
450 severities.add(severity);
451 }
452 catch(Exception e)
453 {
454 Message msg =
455 WARN_ERROR_LOGGER_INVALID_SEVERITY.get(severityName);
456 throw new ConfigException(msg);
457 }
458 }
459 }
460 definedSeverities.put(category, severities);
461 }
462 catch(Exception e)
463 {
464 Message msg = WARN_ERROR_LOGGER_INVALID_CATEGORY.get(categoryName);
465 resultCode = DirectoryServer.getServerErrorResultCode();
466 messages.add(msg);
467 }
468 }
469 }
470 }
471
472 File logFile = getFileForPath(config.getLogFile());
473 FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
474
475 try
476 {
477 FilePermission perm =
478 FilePermission.decodeUNIXMode(config.getLogFilePermissions());
479
480 boolean writerAutoFlush =
481 config.isAutoFlush() && !config.isAsynchronous();
482
483 TextWriter currentWriter;
484 // Determine the writer we are using. If we were writing asyncronously,
485 // we need to modify the underlaying writer.
486 if(writer instanceof AsyncronousTextWriter)
487 {
488 currentWriter = ((AsyncronousTextWriter)writer).getWrappedWriter();
489 }
490 else
491 {
492 currentWriter = writer;
493 }
494
495 if(currentWriter instanceof MultifileTextWriter)
496 {
497 MultifileTextWriter mfWriter = (MultifileTextWriter)writer;
498
499 mfWriter.setNamingPolicy(fnPolicy);
500 mfWriter.setFilePermissions(perm);
501 mfWriter.setAppend(config.isAppend());
502 mfWriter.setAutoFlush(writerAutoFlush);
503 mfWriter.setBufferSize((int)config.getBufferSize());
504 mfWriter.setInterval(config.getTimeInterval());
505
506 mfWriter.removeAllRetentionPolicies();
507 mfWriter.removeAllRotationPolicies();
508
509 for(DN dn : config.getRotationPolicyDNs())
510 {
511 mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
512 }
513
514 for(DN dn: config.getRetentionPolicyDNs())
515 {
516 mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
517 }
518
519 if(writer instanceof AsyncronousTextWriter && !config.isAsynchronous())
520 {
521 // The asynronous setting is being turned off.
522 AsyncronousTextWriter asyncWriter = ((AsyncronousTextWriter)writer);
523 writer = mfWriter;
524 asyncWriter.shutdown(false);
525 }
526
527 if(!(writer instanceof AsyncronousTextWriter) &&
528 config.isAsynchronous())
529 {
530 // The asynronous setting is being turned on.
531 AsyncronousTextWriter asyncWriter =
532 new AsyncronousTextWriter("Asyncronous Text Writer for " +
533 config.dn().toNormalizedString(), config.getQueueSize(),
534 config.isAutoFlush(),
535 mfWriter);
536 writer = asyncWriter;
537 }
538
539 if((currentConfig.isAsynchronous() && config.isAsynchronous()) &&
540 (currentConfig.getQueueSize() != config.getQueueSize()))
541 {
542 adminActionRequired = true;
543 }
544
545 currentConfig = config;
546 }
547 }
548 catch(Exception e)
549 {
550 Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(
551 config.dn().toString(),
552 stackTraceToSingleLineString(e));
553 resultCode = DirectoryServer.getServerErrorResultCode();
554 messages.add(message);
555
556 }
557
558 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
559 }
560
561
562
563 /**
564 * {@inheritDoc}
565 */
566 public void close()
567 {
568 writer.shutdown();
569
570 if(currentConfig != null)
571 {
572 currentConfig.removeFileBasedErrorChangeListener(this);
573 }
574 }
575
576
577 /**
578 * {@inheritDoc}
579 */
580 public void logError(Message message)
581 {
582 Severity severity = message.getDescriptor().getSeverity();
583 Category category = message.getDescriptor().getCategory();
584 int msgId = message.getDescriptor().getId();
585 HashSet<Severity> severities = definedSeverities.get(category);
586 if(severities == null)
587 {
588 severities = defaultSeverities;
589 }
590
591 if(severities.contains(severity))
592 {
593
594 StringBuilder sb = new StringBuilder();
595 sb.append("[");
596 sb.append(TimeThread.getLocalTime());
597 sb.append("] category=").append(category).
598 append(" severity=").append(severity).
599 append(" msgID=").append(msgId).
600 append(" msg=").append(message);
601
602 writer.writeRecord(sb.toString());
603 }
604 }
605
606 /**
607 * {@inheritDoc}
608 */
609 public DN getDN()
610 {
611 if(currentConfig != null)
612 {
613 return currentConfig.dn();
614 }
615 else
616 {
617 return null;
618 }
619 }
620 }
621