]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/log.c
...
[imagemagick] / MagickCore / log.c
index 9e259ee1eceab579cec9fa4dc961deecbae0da9a..4221b318cb2296256c579f63039b01692103cf08 100644 (file)
 %                             MagickCore Log Events                           %
 %                                                                             %
 %                               Software Design                               %
-%                                 John Cristy                                 %
+%                                    Cristy                                   %
 %                                September 2002                               %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 %  obtain a copy of the License at                                            %
 %                                                                             %
-%    http://www.imagemagick.org/script/license.php                            %
+%    https://www.imagemagick.org/script/license.php                           %
 %                                                                             %
 %  Unless required by applicable law or agreed to in writing, software        %
 %  distributed under the License is distributed on an "AS IS" BASIS,          %
 #include "MagickCore/configure-private.h"
 #include "MagickCore/exception.h"
 #include "MagickCore/exception-private.h"
-#include "MagickCore/hashmap.h"
+#include "MagickCore/linked-list.h"
 #include "MagickCore/log.h"
 #include "MagickCore/log-private.h"
 #include "MagickCore/memory_.h"
+#include "MagickCore/nt-base-private.h"
 #include "MagickCore/option.h"
 #include "MagickCore/semaphore.h"
 #include "MagickCore/timer.h"
@@ -62,6 +63,7 @@
 #include "MagickCore/utility-private.h"
 #include "MagickCore/version.h"
 #include "MagickCore/xml-tree.h"
+#include "MagickCore/xml-tree-private.h"
 \f
 /*
   Define declarations.
@@ -80,7 +82,8 @@ typedef enum
   StderrHandler = 0x0004,
   FileHandler = 0x0008,
   DebugHandler = 0x0010,
-  EventHandler = 0x0020
+  EventHandler = 0x0020,
+  MethodHandler = 0x0040
 } LogHandlerType;
 
 typedef struct _EventInfo
@@ -127,7 +130,6 @@ struct _LogInfo
 
   MagickBooleanType
     append,
-    exempt,
     stealth;
 
   TimerInfo
@@ -135,6 +137,9 @@ struct _LogInfo
 
   size_t
     signature;
+
+  MagickLogMethod
+    method;
 };
 
 typedef struct _LogMapInfo
@@ -154,36 +159,58 @@ typedef struct _LogMapInfo
   Static declarations.
 */
 static const HandlerInfo
-  LogHandlers[] =
+  LogHandlers[32] =
   {
-    { "console", ConsoleHandler },
-    { "debug", DebugHandler },
-    { "event", EventHandler },
-    { "file", FileHandler },
-    { "none", NoHandler },
-    { "stderr", StderrHandler },
-    { "stdout", StdoutHandler },
+    { "Console", ConsoleHandler },
+    { "Debug", DebugHandler },
+    { "Event", EventHandler },
+    { "File", FileHandler },
+    { "None", NoHandler },
+    { "Stderr", StderrHandler },
+    { "Stdout", StdoutHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
+    { (char *) NULL, UndefinedHandler },
     { (char *) NULL, UndefinedHandler }
   };
 
 static const LogMapInfo
   LogMap[] =
   {
-    { NoEvents, ConsoleHandler, "Magick-%d.log",
-      "%t %r %u %v %d %c[%p]: %m/%f/%l/%d\n  %e" }
+    { NoEvents, ConsoleHandler, "Magick-%g.log",
+      "%t %r %u %v %d %c[%p]: %m/%f/%l/%d\\n  %e" }
   };
 
 static char
-  log_name[MaxTextExtent] = "Magick";
+  log_name[MagickPathExtent] = "Magick";
 
 static LinkedListInfo
-  *log_list = (LinkedListInfo *) NULL;
+  *log_cache = (LinkedListInfo *) NULL;
 
 static SemaphoreInfo
+  *event_semaphore = (SemaphoreInfo *) NULL,
   *log_semaphore = (SemaphoreInfo *) NULL;
-
-static volatile MagickBooleanType
-  instantiate_log = MagickFalse;
 \f
 /*
   Forward declarations.
@@ -195,8 +222,108 @@ static LogInfo
   *GetLogInfo(const char *,ExceptionInfo *);
 
 static MagickBooleanType
-  InitializeLogList(ExceptionInfo *),
-  LoadLogLists(const char *,ExceptionInfo *);
+  IsLogCacheInstantiated(ExceptionInfo *),
+  LoadLogCache(LinkedListInfo *,const char *,const char *,const size_t,
+    ExceptionInfo *);
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%  A c q u i r e L o g C a c h e                                              %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  AcquireLogCache() caches one or more log configurations which provides a
+%  mapping between log attributes and log name.
+%
+%  The format of the AcquireLogCache method is:
+%
+%      LinkedListInfo *AcquireLogCache(const char *filename,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o filename: the log configuration filename.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+static LinkedListInfo *AcquireLogCache(const char *filename,
+  ExceptionInfo *exception)
+{
+  LinkedListInfo
+    *cache;
+
+  MagickStatusType
+    status;
+
+  register ssize_t
+    i;
+
+  /*
+    Load external log map.
+  */
+  cache=NewLinkedList(0);
+  if (cache == (LinkedListInfo *) NULL)
+    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+  status=MagickTrue;
+#if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
+  {
+    const StringInfo
+      *option;
+
+    LinkedListInfo
+      *options;
+
+    options=GetConfigureOptions(filename,exception);
+    option=(const StringInfo *) GetNextValueInLinkedList(options);
+    while (option != (const StringInfo *) NULL)
+    {
+      status&=LoadLogCache(cache,(const char *) GetStringInfoDatum(option),
+        GetStringInfoPath(option),0,exception);
+      option=(const StringInfo *) GetNextValueInLinkedList(options);
+    }
+    options=DestroyConfigureOptions(options);
+  }
+#endif
+  /*
+    Load built-in log map.
+  */
+  for (i=0; i < (ssize_t) (sizeof(LogMap)/sizeof(*LogMap)); i++)
+  {
+    LogInfo
+      *log_info;
+
+    register const LogMapInfo
+      *p;
+
+    p=LogMap+i;
+    log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
+    if (log_info == (LogInfo *) NULL)
+      {
+        (void) ThrowMagickException(exception,GetMagickModule(),
+          ResourceLimitError,"MemoryAllocationFailed","`%s'",p->filename);
+        continue;
+      }
+    (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
+    log_info->path=ConstantString("[built-in]");
+    GetTimerInfo((TimerInfo *) &log_info->timer);
+    log_info->event_mask=p->event_mask;
+    log_info->handler_mask=p->handler_mask;
+    log_info->filename=ConstantString(p->filename);
+    log_info->format=ConstantString(p->format);
+    log_info->signature=MagickCoreSignature;
+    status&=AppendValueToLinkedList(cache,log_info);
+    if (status == MagickFalse)
+      (void) ThrowMagickException(exception,GetMagickModule(),
+        ResourceLimitError,"MemoryAllocationFailed","`%s'",log_info->name);
+  }
+  return(cache);
+}
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -232,8 +359,7 @@ MagickExport void CloseMagickLog(void)
   LockSemaphoreInfo(log_semaphore);
   if (log_info->file != (FILE *) NULL)
     {
-      if (log_info->append == MagickFalse)
-        (void) FormatLocaleFile(log_info->file,"</log>\n");
+      (void) FormatLocaleFile(log_info->file,"</log>\n");
       (void) fclose(log_info->file);
       log_info->file=(FILE *) NULL;
     }
@@ -271,29 +397,28 @@ static LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
     *p;
 
   assert(exception != (ExceptionInfo *) NULL);
-  if ((log_list == (LinkedListInfo *) NULL) || (instantiate_log == MagickFalse))
-    if (InitializeLogList(exception) == MagickFalse)
-      return((LogInfo *) NULL);
-  if ((log_list == (LinkedListInfo *) NULL) ||
-      (IsLinkedListEmpty(log_list) != MagickFalse))
+  if (IsLogCacheInstantiated(exception) == MagickFalse)
     return((LogInfo *) NULL);
-  if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
-    return((LogInfo *) GetValueFromLinkedList(log_list,0));
   /*
     Search for log tag.
   */
   LockSemaphoreInfo(log_semaphore);
-  ResetLinkedListIterator(log_list);
-  p=(LogInfo *) GetNextValueInLinkedList(log_list);
+  ResetLinkedListIterator(log_cache);
+  p=(LogInfo *) GetNextValueInLinkedList(log_cache);
+  if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+    {
+      UnlockSemaphoreInfo(log_semaphore);
+      return(p);
+    }
   while (p != (LogInfo *) NULL)
   {
     if (LocaleCompare(name,p->name) == 0)
       break;
-    p=(LogInfo *) GetNextValueInLinkedList(log_list);
+    p=(LogInfo *) GetNextValueInLinkedList(log_cache);
   }
   if (p != (LogInfo *) NULL)
-    (void) InsertValueInLinkedList(log_list,0,
-      RemoveElementByValueFromLinkedList(log_list,p));
+    (void) InsertValueInLinkedList(log_cache,0,
+      RemoveElementByValueFromLinkedList(log_cache,p));
   UnlockSemaphoreInfo(log_semaphore);
   return(p);
 }
@@ -369,21 +494,21 @@ MagickExport const LogInfo **GetLogInfoList(const char *pattern,
   if (p == (const LogInfo *) NULL)
     return((const LogInfo **) NULL);
   preferences=(const LogInfo **) AcquireQuantumMemory((size_t)
-    GetNumberOfElementsInLinkedList(log_list)+1UL,sizeof(*preferences));
+    GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
   if (preferences == (const LogInfo **) NULL)
     return((const LogInfo **) NULL);
   /*
     Generate log list.
   */
   LockSemaphoreInfo(log_semaphore);
-  ResetLinkedListIterator(log_list);
-  p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+  ResetLinkedListIterator(log_cache);
+  p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
   for (i=0; p != (const LogInfo *) NULL; )
   {
     if ((p->stealth == MagickFalse) &&
         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
       preferences[i++]=p;
-    p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+    p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
   }
   UnlockSemaphoreInfo(log_semaphore);
   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogInfoCompare);
@@ -439,8 +564,8 @@ static int LogCompare(const void *x,const void *y)
 }
 #endif
 
-MagickExport char **GetLogList(const char *pattern,
-  size_t *number_preferences,ExceptionInfo *exception)
+MagickExport char **GetLogList(const char *pattern,size_t *number_preferences,
+  ExceptionInfo *exception)
 {
   char
     **preferences;
@@ -462,21 +587,21 @@ MagickExport char **GetLogList(const char *pattern,
   if (p == (const LogInfo *) NULL)
     return((char **) NULL);
   preferences=(char **) AcquireQuantumMemory((size_t)
-    GetNumberOfElementsInLinkedList(log_list)+1UL,sizeof(*preferences));
+    GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
   if (preferences == (char **) NULL)
     return((char **) NULL);
   /*
     Generate log list.
   */
   LockSemaphoreInfo(log_semaphore);
-  ResetLinkedListIterator(log_list);
-  p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+  ResetLinkedListIterator(log_cache);
+  p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
   for (i=0; p != (const LogInfo *) NULL; )
   {
     if ((p->stealth == MagickFalse) &&
         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
       preferences[i++]=ConstantString(p->name);
-    p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+    p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
   }
   UnlockSemaphoreInfo(log_semaphore);
   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogCompare);
@@ -513,39 +638,36 @@ MagickExport const char *GetLogName(void)
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+   I n i t i a l i z e L o g L i s t                                         %
++   I s L o g C a c h e I n s t a n t i a t e d                               %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  InitializeLogList() initialize the log list.
+%  IsLogCacheInstantiated() determines if the log list is instantiated.  If
+%  not, it instantiates the list and returns it.
 %
-%  The format of the InitializeLogList method is:
+%  The format of the IsLogInstantiated method is:
 %
-%      MagickBooleanType InitializeLogList(ExceptionInfo *exception)
+%      MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
 %
 %  A description of each parameter follows.
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
-static MagickBooleanType InitializeLogList(ExceptionInfo *exception)
+static MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
 {
-  if ((log_list == (LinkedListInfo *) NULL) && (instantiate_log == MagickFalse))
+  if (log_cache == (LinkedListInfo *) NULL)
     {
       if (log_semaphore == (SemaphoreInfo *) NULL)
-        AcquireSemaphoreInfo(&log_semaphore);
+        ActivateSemaphoreInfo(&log_semaphore);
       LockSemaphoreInfo(log_semaphore);
-      if ((log_list == (LinkedListInfo *) NULL) &&
-          (instantiate_log == MagickFalse))
-        {
-          (void) LoadLogLists(LogFilename,exception);
-          instantiate_log=MagickTrue;
-        }
+      if (log_cache == (LinkedListInfo *) NULL)
+        log_cache=AcquireLogCache(LogFilename,exception);
       UnlockSemaphoreInfo(log_semaphore);
     }
-  return(log_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+  return(log_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
 }
 \f
 /*
@@ -575,8 +697,8 @@ MagickExport MagickBooleanType IsEventLogging(void)
   ExceptionInfo
     *exception;
 
-  if ((log_list == (LinkedListInfo *) NULL) ||
-      (IsLinkedListEmpty(log_list) != MagickFalse))
+  if ((log_cache == (LinkedListInfo *) NULL) ||
+      (IsLinkedListEmpty(log_cache) != MagickFalse))
     return(MagickFalse);
   exception=AcquireExceptionInfo();
   log_info=GetLogInfo("*",exception);
@@ -611,9 +733,6 @@ MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
 {
 #define MegabytesToBytes(value) ((MagickSizeType) (value)*1024*1024)
 
-  char
-    limit[MaxTextExtent];
-
   const char
     *path;
 
@@ -643,13 +762,32 @@ MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
     if ((path == (const char *) NULL) ||
         (LocaleCompare(path,log_info[i]->path) != 0))
       {
+        size_t
+          length;
+
         if (log_info[i]->path != (char *) NULL)
           (void) FormatLocaleFile(file,"\nPath: %s\n\n",log_info[i]->path);
-        (void) FormatLocaleFile(file,
-          "Filename       Generations     Limit  Format\n");
-        (void) FormatLocaleFile(file,
-          "-------------------------------------------------"
-          "------------------------------\n");
+        length=0;
+        for (j=0; j < (ssize_t) (8*sizeof(LogHandlerType)); j++)
+        {
+          size_t
+            mask;
+
+          if (LogHandlers[j].name == (const char *) NULL)
+            break;
+          mask=1;
+          mask<<=j;
+          if ((log_info[i]->handler_mask & mask) != 0)
+            {
+              (void) FormatLocaleFile(file,"%s ",LogHandlers[j].name);
+              length+=strlen(LogHandlers[j].name);
+            }
+        }
+        for (j=(ssize_t) length; j <= 12; j++)
+          (void) FormatLocaleFile(file," ");
+        (void) FormatLocaleFile(file," Generations     Limit  Format\n");
+        (void) FormatLocaleFile(file,"-----------------------------------------"
+          "--------------------------------------\n");
       }
     path=log_info[i]->path;
     if (log_info[i]->filename != (char *) NULL)
@@ -659,9 +797,7 @@ MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
           (void) FormatLocaleFile(file," ");
       }
     (void) FormatLocaleFile(file,"%9g  ",(double) log_info[i]->generations);
-    (void) FormatMagickSize(MegabytesToBytes(log_info[i]->limit),MagickFalse,
-      limit);
-    (void) FormatLocaleFile(file,"%8sB  ",limit);
+    (void) FormatLocaleFile(file,"%8g   ",(double) log_info[i]->limit);
     if (log_info[i]->format != (char *) NULL)
       (void) FormatLocaleFile(file,"%s",log_info[i]->format);
     (void) FormatLocaleFile(file,"\n");
@@ -691,7 +827,15 @@ MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
 */
 MagickPrivate MagickBooleanType LogComponentGenesis(void)
 {
-  AcquireSemaphoreInfo(&log_semaphore);
+  ExceptionInfo
+    *exception;
+
+  if (log_semaphore == (SemaphoreInfo *) NULL)
+    log_semaphore=AcquireSemaphoreInfo();
+  exception=AcquireExceptionInfo();
+  (void) GetLogInfo("*",exception);
+  exception=DestroyExceptionInfo(exception);
+  event_semaphore=AcquireSemaphoreInfo();
   return(MagickTrue);
 }
 \f
@@ -722,34 +866,34 @@ static void *DestroyLogElement(void *log_info)
   p=(LogInfo *) log_info;
   if (p->file != (FILE *) NULL)
     {
-      if (p->append == MagickFalse)
-        (void) FormatLocaleFile(p->file,"</log>\n");
+      (void) FormatLocaleFile(p->file,"</log>\n");
       (void) fclose(p->file);
       p->file=(FILE *) NULL;
     }
-  if (p->exempt == MagickFalse)
-    {
-      if (p->format != (char *) NULL)
-        p->format=DestroyString(p->format);
-      if (p->path != (char *) NULL)
-        p->path=DestroyString(p->path);
-      if (p->filename != (char *) NULL)
-        p->filename=DestroyString(p->filename);
-    }
+  if (p->format != (char *) NULL)
+    p->format=DestroyString(p->format);
+  if (p->path != (char *) NULL)
+    p->path=DestroyString(p->path);
+  if (p->filename != (char *) NULL)
+    p->filename=DestroyString(p->filename);
   p=(LogInfo *) RelinquishMagickMemory(p);
   return((void *) NULL);
 }
 
 MagickPrivate void LogComponentTerminus(void)
 {
+  if (event_semaphore == (SemaphoreInfo *) NULL)
+    ActivateSemaphoreInfo(&event_semaphore);
+  LockSemaphoreInfo(event_semaphore);
+  UnlockSemaphoreInfo(event_semaphore);
+  RelinquishSemaphoreInfo(&event_semaphore);
   if (log_semaphore == (SemaphoreInfo *) NULL)
-    AcquireSemaphoreInfo(&log_semaphore);
+    ActivateSemaphoreInfo(&log_semaphore);
   LockSemaphoreInfo(log_semaphore);
-  if (log_list != (LinkedListInfo *) NULL)
-    log_list=DestroyLinkedList(log_list,DestroyLogElement);
-  instantiate_log=MagickFalse;
+  if (log_cache != (LinkedListInfo *) NULL)
+    log_cache=DestroyLinkedList(log_cache,DestroyLogElement);
   UnlockSemaphoreInfo(log_semaphore);
-  DestroySemaphoreInfo(&log_semaphore);
+  RelinquishSemaphoreInfo(&log_semaphore);
 }
 \f
 /*
@@ -785,9 +929,8 @@ MagickPrivate void LogComponentTerminus(void)
 %    o format: the output format.
 %
 */
-static char *TranslateEvent(const LogEventType magick_unused(type),
-  const char *module,const char *function,const size_t line,
-  const char *domain,const char *event)
+static char *TranslateEvent(const char *module,const char *function,
+  const size_t line,const char *domain,const char *event)
 {
   char
     *text;
@@ -823,11 +966,11 @@ static char *TranslateEvent(const LogEventType magick_unused(type),
   text=AcquireString(event);
   if (log_info->format == (char *) NULL)
     return(text);
-  extent=strlen(event)+MaxTextExtent;
+  extent=strlen(event)+MagickPathExtent;
   if (LocaleCompare(log_info->format,"xml") == 0)
     {
       char
-        timestamp[MaxTextExtent];
+        timestamp[MagickPathExtent];
 
       /*
         Translate event in "XML" format.
@@ -859,10 +1002,10 @@ static char *TranslateEvent(const LogEventType magick_unused(type),
   for (p=log_info->format; *p != '\0'; p++)
   {
     *q='\0';
-    if ((size_t) (q-text+MaxTextExtent) >= extent)
+    if ((size_t) (q-text+MagickPathExtent) >= extent)
       {
-        extent+=MaxTextExtent;
-        text=(char *) ResizeQuantumMemory(text,extent+MaxTextExtent,
+        extent+=MagickPathExtent;
+        text=(char *) ResizeQuantumMemory(text,extent+MagickPathExtent,
           sizeof(*text));
         if (text == (char *) NULL)
           return((char *) NULL);
@@ -1028,15 +1171,15 @@ static char *TranslateFilename(const LogInfo *log_info)
   assert(log_info != (LogInfo *) NULL);
   assert(log_info->filename != (char *) NULL);
   filename=AcquireString((char *) NULL);
-  extent=MaxTextExtent;
+  extent=MagickPathExtent;
   q=filename;
   for (p=log_info->filename; *p != '\0'; p++)
   {
     *q='\0';
-    if ((size_t) (q-filename+MaxTextExtent) >= extent)
+    if ((size_t) (q-filename+MagickPathExtent) >= extent)
       {
-        extent+=MaxTextExtent;
-        filename=(char *) ResizeQuantumMemory(filename,extent+MaxTextExtent,
+        extent+=MagickPathExtent;
+        filename=(char *) ResizeQuantumMemory(filename,extent+MagickPathExtent,
           sizeof(*filename));
         if (filename == (char *) NULL)
           return((char *) NULL);
@@ -1110,11 +1253,10 @@ static char *TranslateFilename(const LogInfo *log_info)
 }
 
 MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
-  const char *function,const size_t line,const char *format,
-  va_list operands)
+  const char *function,const size_t line,const char *format,va_list operands)
 {
   char
-    event[MaxTextExtent],
+    event[MagickPathExtent],
     *text;
 
   const char
@@ -1134,25 +1276,27 @@ MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
   exception=AcquireExceptionInfo();
   log_info=(LogInfo *) GetLogInfo("*",exception);
   exception=DestroyExceptionInfo(exception);
-  LockSemaphoreInfo(log_semaphore);
+  if (event_semaphore == (SemaphoreInfo *) NULL)
+    ActivateSemaphoreInfo(&event_semaphore);
+  LockSemaphoreInfo(event_semaphore);
   if ((log_info->event_mask & type) == 0)
     {
-      UnlockSemaphoreInfo(log_semaphore);
+      UnlockSemaphoreInfo(event_semaphore);
       return(MagickTrue);
     }
   domain=CommandOptionToMnemonic(MagickLogEventOptions,type);
 #if defined(MAGICKCORE_HAVE_VSNPRINTF)
-  n=vsnprintf(event,MaxTextExtent,format,operands);
+  n=vsnprintf(event,MagickPathExtent,format,operands);
 #else
   n=vsprintf(event,format,operands);
 #endif
   if (n < 0)
-    event[MaxTextExtent-1]='\0';
-  text=TranslateEvent(type,module,function,line,domain,event);
+    event[MagickPathExtent-1]='\0';
+  text=TranslateEvent(module,function,line,domain,event);
   if (text == (char *) NULL)
     {
       (void) ContinueTimer((TimerInfo *) &log_info->timer);
-      UnlockSemaphoreInfo(log_semaphore);
+      UnlockSemaphoreInfo(event_semaphore);
       return(MagickFalse);
     }
   if ((log_info->handler_mask & ConsoleHandler) != 0)
@@ -1164,6 +1308,7 @@ MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
     {
 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
       OutputDebugString(text);
+      OutputDebugString("\n");
 #endif
     }
   if ((log_info->handler_mask & EventHandler) != 0)
@@ -1195,7 +1340,7 @@ MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
           if (filename == (char *) NULL)
             {
               (void) ContinueTimer((TimerInfo *) &log_info->timer);
-              UnlockSemaphoreInfo(log_semaphore);
+              UnlockSemaphoreInfo(event_semaphore);
               return(MagickFalse);
             }
           log_info->append=IsPathAccessible(filename);
@@ -1203,20 +1348,23 @@ MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
           filename=(char  *) RelinquishMagickMemory(filename);
           if (log_info->file == (FILE *) NULL)
             {
-              UnlockSemaphoreInfo(log_semaphore);
+              UnlockSemaphoreInfo(event_semaphore);
               return(MagickFalse);
             }
           log_info->generation++;
           if (log_info->append == MagickFalse)
-            {
-              (void) FormatLocaleFile(log_info->file,"<?xml version=\"1.0\" "
-                "encoding=\"UTF-8\" standalone=\"yes\"?>\n");
-              (void) FormatLocaleFile(log_info->file,"<log>\n");
-            }
+            (void) FormatLocaleFile(log_info->file,"<?xml version=\"1.0\" "
+              "encoding=\"UTF-8\" standalone=\"yes\"?>\n");
+          (void) FormatLocaleFile(log_info->file,"<log>\n");
         }
-      (void) FormatLocaleFile(log_info->file,"%s\n",text);
+      (void) FormatLocaleFile(log_info->file,"  <event>%s</event>\n",text);
       (void) fflush(log_info->file);
     }
+  if ((log_info->handler_mask & MethodHandler) != 0)
+    {
+      if (log_info->method != (MagickLogMethod) NULL)
+        log_info->method(type,text);
+    }
   if ((log_info->handler_mask & StdoutHandler) != 0)
     {
       (void) FormatLocaleFile(stdout,"%s\n",text);
@@ -1229,7 +1377,7 @@ MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
     }
   text=(char  *) RelinquishMagickMemory(text);
   (void) ContinueTimer((TimerInfo *) &log_info->timer);
-  UnlockSemaphoreInfo(log_semaphore);
+  UnlockSemaphoreInfo(event_semaphore);
   return(MagickTrue);
 }
 
@@ -1253,19 +1401,19 @@ MagickBooleanType LogMagickEvent(const LogEventType type,const char *module,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+   L o a d L o g L i s t                                                     %
++   L o a d L o g C a c h e                                                   %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  LoadLogList() loads the log configuration file which provides a
+%  LoadLogCache() loads the log configurations which provides a
 %  mapping between log attributes and log name.
 %
-%  The format of the LoadLogList method is:
+%  The format of the LoadLogCache method is:
 %
-%      MagickBooleanType LoadLogList(const char *xml,const char *filename,
-%        const size_t depth,ExceptionInfo *exception)
+%      MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
+%        const char *filename,const size_t depth,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -1278,11 +1426,11 @@ MagickBooleanType LogMagickEvent(const LogEventType type,const char *module,
 %    o exception: return any errors or warnings in this structure.
 %
 */
-static MagickBooleanType LoadLogList(const char *xml,const char *filename,
-  const size_t depth,ExceptionInfo *exception)
+static MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
+  const char *filename,const size_t depth,ExceptionInfo *exception)
 {
   char
-    keyword[MaxTextExtent],
+    keyword[MagickPathExtent],
     *token;
 
   const char
@@ -1294,39 +1442,33 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
   MagickStatusType
     status;
 
+  size_t
+    extent;
+
   /*
     Load the log map file.
   */
   if (xml == (const char *) NULL)
     return(MagickFalse);
-  if (log_list == (LinkedListInfo *) NULL)
-    {
-      log_list=NewLinkedList(0);
-      if (log_list == (LinkedListInfo *) NULL)
-        {
-          ThrowFileException(exception,ResourceLimitError,
-            "MemoryAllocationFailed",filename);
-          return(MagickFalse);
-        }
-    }
   status=MagickTrue;
-  token=AcquireString((const char *) xml);
+  token=AcquireString(xml);
+  extent=strlen(token)+MagickPathExtent;
   for (q=(const char *) xml; *q != '\0'; )
   {
     /*
       Interpret XML.
     */
-    GetMagickToken(q,&q,token);
+    GetNextToken(q,&q,extent,token);
     if (*token == '\0')
       break;
-    (void) CopyMagickString(keyword,token,MaxTextExtent);
+    (void) CopyMagickString(keyword,token,MagickPathExtent);
     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
       {
         /*
           Doctype element.
         */
         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
-          GetMagickToken(q,&q,token);
+          GetNextToken(q,&q,extent,token);
         continue;
       }
     if (LocaleNCompare(keyword,"<!--",4) == 0)
@@ -1335,7 +1477,7 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
           Comment element.
         */
         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
-          GetMagickToken(q,&q,token);
+          GetNextToken(q,&q,extent,token);
         continue;
       }
     if (LocaleCompare(keyword,"<include") == 0)
@@ -1345,11 +1487,11 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
         */
         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
         {
-          (void) CopyMagickString(keyword,token,MaxTextExtent);
-          GetMagickToken(q,&q,token);
+          (void) CopyMagickString(keyword,token,MagickPathExtent);
+          GetNextToken(q,&q,extent,token);
           if (*token != '=')
             continue;
-          GetMagickToken(q,&q,token);
+          GetNextToken(q,&q,extent,token);
           if (LocaleCompare(keyword,"file") == 0)
             {
               if (depth > 200)
@@ -1358,22 +1500,23 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
               else
                 {
                   char
-                    path[MaxTextExtent],
-                    *xml;
+                    path[MagickPathExtent],
+                    *file_xml;
 
                   GetPathComponent(filename,HeadPath,path);
                   if (*path != '\0')
                     (void) ConcatenateMagickString(path,DirectorySeparator,
-                      MaxTextExtent);
+                      MagickPathExtent);
                   if (*token == *DirectorySeparator)
-                    (void) CopyMagickString(path,token,MaxTextExtent);
+                    (void) CopyMagickString(path,token,MagickPathExtent);
                   else
-                    (void) ConcatenateMagickString(path,token,MaxTextExtent);
-                  xml=FileToString(path,~0,exception);
-                  if (xml != (char *) NULL)
+                    (void) ConcatenateMagickString(path,token,MagickPathExtent);
+                  file_xml=FileToXML(path,~0UL);
+                  if (file_xml != (char *) NULL)
                     {
-                      status|=LoadLogList(xml,path,depth+1,exception);
-                      xml=DestroyString(xml);
+                      status&=LoadLogCache(cache,file_xml,path,depth+1,
+                        exception);
+                      file_xml=DestroyString(file_xml);
                     }
                 }
             }
@@ -1391,25 +1534,25 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
         (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
         log_info->path=ConstantString(filename);
         GetTimerInfo((TimerInfo *) &log_info->timer);
-        log_info->exempt=MagickFalse;
-        log_info->signature=MagickSignature;
+        log_info->signature=MagickCoreSignature;
         continue;
       }
     if (log_info == (LogInfo *) NULL)
       continue;
     if (LocaleCompare(keyword,"</logmap>") == 0)
       {
-        status=AppendValueToLinkedList(log_list,log_info);
+        status=AppendValueToLinkedList(cache,log_info);
         if (status == MagickFalse)
           (void) ThrowMagickException(exception,GetMagickModule(),
             ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
         log_info=(LogInfo *) NULL;
+        continue;
       }
-    GetMagickToken(q,(const char **) NULL,token);
+    GetNextToken(q,(const char **) NULL,extent,token);
     if (*token != '=')
       continue;
-    GetMagickToken(q,&q,token);
-    GetMagickToken(q,&q,token);
+    GetNextToken(q,&q,extent,token);
+    GetNextToken(q,&q,extent,token);
     switch (*keyword)
     {
       case 'E':
@@ -1490,7 +1633,7 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
     }
   }
   token=DestroyString(token);
-  if (log_list == (LinkedListInfo *) NULL)
+  if (cache == (LinkedListInfo *) NULL)
     return(MagickFalse);
   return(status != 0 ? MagickTrue : MagickFalse);
 }
@@ -1500,106 +1643,6 @@ static MagickBooleanType LoadLogList(const char *xml,const char *filename,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%  L o a d L o g L i s t s                                                    %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  LoadLogLists() loads one or more log configuration file which provides a
-%  mapping between log attributes and log name.
-%
-%  The format of the LoadLogLists method is:
-%
-%      MagickBooleanType LoadLogLists(const char *filename,
-%        ExceptionInfo *exception)
-%
-%  A description of each parameter follows:
-%
-%    o filename: the log configuration filename.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-static MagickBooleanType LoadLogLists(const char *filename,
-  ExceptionInfo *exception)
-{
-  const StringInfo
-    *option;
-
-  LinkedListInfo
-    *options;
-
-  MagickStatusType
-    status;
-
-  register ssize_t
-    i;
-
-  /*
-    Load built-in log map.
-  */
-  status=MagickFalse;
-  if (log_list == (LinkedListInfo *) NULL)
-    {
-      log_list=NewLinkedList(0);
-      if (log_list == (LinkedListInfo *) NULL)
-        {
-          ThrowFileException(exception,ResourceLimitError,
-            "MemoryAllocationFailed",filename);
-          return(MagickFalse);
-        }
-    }
-  for (i=0; i < (ssize_t) (sizeof(LogMap)/sizeof(*LogMap)); i++)
-  {
-    LogInfo
-      *log_info;
-
-    register const LogMapInfo
-      *p;
-
-    p=LogMap+i;
-    log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
-    if (log_info == (LogInfo *) NULL)
-      {
-        (void) ThrowMagickException(exception,GetMagickModule(),
-          ResourceLimitError,"MemoryAllocationFailed","`%s'",log_info->name);
-        continue;
-      }
-    (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
-    log_info->path=(char *) "[built-in]";
-    GetTimerInfo((TimerInfo *) &log_info->timer);
-    log_info->event_mask=p->event_mask;
-    log_info->handler_mask=p->handler_mask;
-    log_info->filename=ConstantString(p->filename);
-    log_info->format=ConstantString(p->format);
-    log_info->exempt=MagickTrue;
-    log_info->signature=MagickSignature;
-    status=AppendValueToLinkedList(log_list,log_info);
-    if (status == MagickFalse)
-      (void) ThrowMagickException(exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",log_info->name);
-  }
-  /*
-    Load external log map.
-  */
-  options=GetConfigureOptions(filename,exception);
-  option=(const StringInfo *) GetNextValueInLinkedList(options);
-  while (option != (const StringInfo *) NULL)
-  {
-    status|=LoadLogList((const char *) GetStringInfoDatum(option),
-      GetStringInfoPath(option),0,exception);
-    option=(const StringInfo *) GetNextValueInLinkedList(options);
-  }
-  options=DestroyConfigureOptions(options);
-  return(status != 0 ? MagickTrue : MagickFalse);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
 +   P a r s e L o g H a n d l e r s                                           %
 %                                                                             %
 %                                                                             %
@@ -1693,7 +1736,7 @@ MagickExport LogEventType SetLogEventMask(const char *events)
   exception=DestroyExceptionInfo(exception);
   option=ParseCommandOption(MagickLogEventOptions,MagickTrue,events);
   LockSemaphoreInfo(log_semaphore);
-  log_info=(LogInfo *) GetValueFromLinkedList(log_list,0);
+  log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
   log_info->event_mask=(LogEventType) option;
   if (option == -1)
     log_info->event_mask=UndefinedEvents;
@@ -1740,6 +1783,48 @@ MagickExport void SetLogFormat(const char *format)
   log_info->format=ConstantString(format);
   UnlockSemaphoreInfo(log_semaphore);
 }
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   S e t L o g M e t h o d                                                   %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  SetLogMethod() sets the method that will be called when an event is logged.
+%
+%  The format of the SetLogMethod method is:
+%
+%      void SetLogMethod(MagickLogMethod method)
+%
+%  A description of each parameter follows:
+%
+%    o method: pointer to a method that will be called when LogMagickEvent is
+%      being called.
+%
+*/
+MagickExport void SetLogMethod(MagickLogMethod method)
+{
+  ExceptionInfo
+    *exception;
+
+  LogInfo
+    *log_info;
+
+  exception=AcquireExceptionInfo();
+  log_info=(LogInfo *) GetLogInfo("*",exception);
+  exception=DestroyExceptionInfo(exception);
+  LockSemaphoreInfo(log_semaphore);
+  log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
+  log_info->handler_mask=(LogHandlerType) (log_info->handler_mask |
+    MethodHandler);
+  log_info->method=method;
+  UnlockSemaphoreInfo(log_semaphore);
+}
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1768,6 +1853,6 @@ MagickExport void SetLogFormat(const char *format)
 MagickExport const char *SetLogName(const char *name)
 {
   if ((name != (char *) NULL) && (*name != '\0'))
-    (void) CopyMagickString(log_name,name,MaxTextExtent);
+    (void) CopyMagickString(log_name,name,MagickPathExtent);
   return(log_name);
 }