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