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.debug; 028 import org.opends.messages.Message; 029 030 import org.opends.server.api.*; 031 import org.opends.server.loggers.*; 032 import org.opends.server.types.*; 033 import org.opends.server.util.ServerConstants; 034 import org.opends.server.util.StaticUtils; 035 import org.opends.server.util.TimeThread; 036 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; 037 import static org.opends.server.util.StaticUtils.getFileForPath; 038 import static org.opends.server.util.ServerConstants.PROPERTY_DEBUG_TARGET; 039 import org.opends.server.admin.std.server.DebugTargetCfg; 040 import org.opends.server.admin.std.server.FileBasedDebugLogPublisherCfg; 041 import org.opends.server.admin.std.server.DebugLogPublisherCfg; 042 import org.opends.server.admin.std.meta.DebugLogPublisherCfgDefn; 043 import org.opends.server.admin.server.ConfigurationChangeListener; 044 import org.opends.server.admin.server.ConfigurationDeleteListener; 045 import org.opends.server.admin.server.ConfigurationAddListener; 046 import org.opends.server.config.ConfigException; 047 import org.opends.server.core.DirectoryServer; 048 import static org.opends.messages.ConfigMessages. 049 ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER; 050 import static org.opends.messages.ConfigMessages. 051 ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE; 052 import static org.opends.messages.ConfigMessages. 053 ERR_CONFIG_LOGGING_INSANE_MODE; 054 import static org.opends.messages.ConfigMessages. 055 ERR_CONFIG_LOGGING_MODE_INVALID; 056 057 058 import java.util.*; 059 import java.io.File; 060 import java.io.IOException; 061 062 import com.sleepycat.je.*; 063 064 /** 065 * The debug log publisher implementation that writes debug messages to files 066 * on disk. It also maintains the rotation and retention polices of the log 067 * files. 068 */ 069 public class TextDebugLogPublisher 070 extends DebugLogPublisher<FileBasedDebugLogPublisherCfg> 071 implements ConfigurationChangeListener<FileBasedDebugLogPublisherCfg>, 072 ConfigurationAddListener<DebugTargetCfg>, 073 ConfigurationDeleteListener<DebugTargetCfg> 074 { 075 private static long globalSequenceNumber; 076 077 private TextWriter writer; 078 079 private FileBasedDebugLogPublisherCfg currentConfig; 080 081 /** 082 * Returns an instance of the text debug log publisher that will print 083 * all messages to the provided writer. This is used to print the messages 084 * to the console when the server starts up. By default, only error level 085 * messages are printed. Special debug targets are also parsed from 086 * system properties if any are specified. 087 * 088 * @param writer The text writer where the message will be written to. 089 * @return The instance of the text error log publisher that will print 090 * all messages to standard out. 091 */ 092 public static TextDebugLogPublisher 093 getStartupTextDebugPublisher(TextWriter writer) 094 { 095 TextDebugLogPublisher startupPublisher = new TextDebugLogPublisher(); 096 startupPublisher.writer = writer; 097 098 Set<Map.Entry<Object, Object>> propertyEntries = 099 System.getProperties().entrySet(); 100 for(Map.Entry<Object, Object> entry : propertyEntries) 101 { 102 if(((String)entry.getKey()).startsWith(PROPERTY_DEBUG_TARGET)) 103 { 104 String value = (String)entry.getValue(); 105 int settingsStart= value.indexOf(":"); 106 107 //See if the scope and settings exists 108 if(settingsStart > 0) 109 { 110 String scope = value.substring(0, settingsStart); 111 TraceSettings settings = 112 TraceSettings.parseTraceSettings( 113 value.substring(settingsStart+1)); 114 if(settings != null) 115 { 116 startupPublisher.addTraceSettings(scope, settings); 117 } 118 } 119 } 120 } 121 122 return startupPublisher; 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 public boolean isConfigurationAcceptable(DebugLogPublisherCfg configuration, 129 List<Message> unacceptableReasons) 130 { 131 FileBasedDebugLogPublisherCfg config = 132 (FileBasedDebugLogPublisherCfg) configuration; 133 return isConfigurationChangeAcceptable(config, unacceptableReasons); 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 public void initializeDebugLogPublisher(FileBasedDebugLogPublisherCfg config) 140 throws ConfigException, InitializationException 141 { 142 File logFile = getFileForPath(config.getLogFile()); 143 FileNamingPolicy fnPolicy = new TimeStampNaming(logFile); 144 145 try 146 { 147 FilePermission perm = 148 FilePermission.decodeUNIXMode(config.getLogFilePermissions()); 149 150 LogPublisherErrorHandler errorHandler = 151 new LogPublisherErrorHandler(config.dn()); 152 153 boolean writerAutoFlush = 154 config.isAutoFlush() && !config.isAsynchronous(); 155 156 MultifileTextWriter writer = 157 new MultifileTextWriter("Multifile Text Writer for " + 158 config.dn().toNormalizedString(), 159 config.getTimeInterval(), 160 fnPolicy, 161 perm, 162 errorHandler, 163 "UTF-8", 164 writerAutoFlush, 165 config.isAppend(), 166 (int)config.getBufferSize()); 167 168 // Validate retention and rotation policies. 169 for(DN dn : config.getRotationPolicyDNs()) 170 { 171 writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn)); 172 } 173 174 for(DN dn: config.getRetentionPolicyDNs()) 175 { 176 writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn)); 177 } 178 179 if(config.isAsynchronous()) 180 { 181 this.writer = new AsyncronousTextWriter("Asyncronous Text Writer for " + 182 config.dn().toNormalizedString(), config.getQueueSize(), 183 config.isAutoFlush(), writer); 184 } 185 else 186 { 187 this.writer = writer; 188 } 189 } 190 catch(DirectoryException e) 191 { 192 Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get( 193 config.dn().toString(), String.valueOf(e)); 194 throw new InitializationException(message, e); 195 196 } 197 catch(IOException e) 198 { 199 Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get( 200 logFile.toString(), config.dn().toString(), String.valueOf(e)); 201 throw new InitializationException(message, e); 202 203 } 204 205 206 config.addDebugTargetAddListener(this); 207 config.addDebugTargetDeleteListener(this); 208 209 //Get the default/global settings 210 LogLevel logLevel = 211 DebugLogLevel.parse(config.getDefaultDebugLevel().toString()); 212 Set<LogCategory> logCategories = null; 213 if(!config.getDefaultDebugCategory().isEmpty()) 214 { 215 logCategories = 216 new HashSet<LogCategory>(config.getDefaultDebugCategory().size()); 217 for(DebugLogPublisherCfgDefn.DefaultDebugCategory category : 218 config.getDefaultDebugCategory()) 219 { 220 logCategories.add(DebugLogCategory.parse(category.toString())); 221 } 222 } 223 224 TraceSettings defaultSettings = 225 new TraceSettings(logLevel, logCategories, 226 config.isDefaultOmitMethodEntryArguments(), 227 config.isDefaultOmitMethodReturnValue(), 228 config.getDefaultThrowableStackFrames(), 229 config.isDefaultIncludeThrowableCause()); 230 231 addTraceSettings(null, defaultSettings); 232 233 for(String name : config.listDebugTargets()) 234 { 235 DebugTargetCfg targetCfg = config.getDebugTarget(name); 236 237 addTraceSettings(targetCfg.getDebugScope(), new TraceSettings(targetCfg)); 238 } 239 240 currentConfig = config; 241 242 config.addFileBasedDebugChangeListener(this); 243 } 244 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 public boolean isConfigurationChangeAcceptable( 251 FileBasedDebugLogPublisherCfg config, List<Message> unacceptableReasons) 252 { 253 // Make sure the permission is valid. 254 try 255 { 256 FilePermission filePerm = 257 FilePermission.decodeUNIXMode(config.getLogFilePermissions()); 258 if(!filePerm.isOwnerWritable()) 259 { 260 Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get( 261 config.getLogFilePermissions()); 262 unacceptableReasons.add(message); 263 return false; 264 } 265 } 266 catch(DirectoryException e) 267 { 268 Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get( 269 config.getLogFilePermissions(), String.valueOf(e)); 270 unacceptableReasons.add(message); 271 return false; 272 } 273 274 return true; 275 } 276 277 /** 278 * {@inheritDoc} 279 */ 280 public ConfigChangeResult applyConfigurationChange( 281 FileBasedDebugLogPublisherCfg config) 282 { 283 // Default result code. 284 ResultCode resultCode = ResultCode.SUCCESS; 285 boolean adminActionRequired = false; 286 ArrayList<Message> messages = new ArrayList<Message>(); 287 288 //Get the default/global settings 289 LogLevel logLevel = 290 DebugLogLevel.parse(config.getDefaultDebugLevel().toString()); 291 Set<LogCategory> logCategories = null; 292 if(!config.getDefaultDebugCategory().isEmpty()) 293 { 294 logCategories = 295 new HashSet<LogCategory>(config.getDefaultDebugCategory().size()); 296 for(DebugLogPublisherCfgDefn.DefaultDebugCategory category : 297 config.getDefaultDebugCategory()) 298 { 299 logCategories.add(DebugLogCategory.parse(category.toString())); 300 } 301 } 302 303 TraceSettings defaultSettings = 304 new TraceSettings(logLevel, logCategories, 305 config.isDefaultOmitMethodEntryArguments(), 306 config.isDefaultOmitMethodReturnValue(), 307 config.getDefaultThrowableStackFrames(), 308 config.isDefaultIncludeThrowableCause()); 309 310 addTraceSettings(null, defaultSettings); 311 312 DebugLogger.updateTracerSettings(); 313 314 File logFile = getFileForPath(config.getLogFile()); 315 FileNamingPolicy fnPolicy = new TimeStampNaming(logFile); 316 317 try 318 { 319 FilePermission perm = 320 FilePermission.decodeUNIXMode(config.getLogFilePermissions()); 321 322 boolean writerAutoFlush = 323 config.isAutoFlush() && !config.isAsynchronous(); 324 325 TextWriter currentWriter; 326 // Determine the writer we are using. If we were writing asyncronously, 327 // we need to modify the underlaying writer. 328 if(writer instanceof AsyncronousTextWriter) 329 { 330 currentWriter = ((AsyncronousTextWriter)writer).getWrappedWriter(); 331 } 332 else 333 { 334 currentWriter = writer; 335 } 336 337 if(currentWriter instanceof MultifileTextWriter) 338 { 339 MultifileTextWriter mfWriter = (MultifileTextWriter)writer; 340 341 mfWriter.setNamingPolicy(fnPolicy); 342 mfWriter.setFilePermissions(perm); 343 mfWriter.setAppend(config.isAppend()); 344 mfWriter.setAutoFlush(writerAutoFlush); 345 mfWriter.setBufferSize((int)config.getBufferSize()); 346 mfWriter.setInterval(config.getTimeInterval()); 347 348 mfWriter.removeAllRetentionPolicies(); 349 mfWriter.removeAllRotationPolicies(); 350 351 for(DN dn : config.getRotationPolicyDNs()) 352 { 353 mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn)); 354 } 355 356 for(DN dn: config.getRetentionPolicyDNs()) 357 { 358 mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn)); 359 } 360 361 if(writer instanceof AsyncronousTextWriter && !config.isAsynchronous()) 362 { 363 // The asynronous setting is being turned off. 364 AsyncronousTextWriter asyncWriter = ((AsyncronousTextWriter)writer); 365 writer = mfWriter; 366 asyncWriter.shutdown(false); 367 } 368 369 if(!(writer instanceof AsyncronousTextWriter) && 370 config.isAsynchronous()) 371 { 372 // The asynronous setting is being turned on. 373 AsyncronousTextWriter asyncWriter = 374 new AsyncronousTextWriter("Asyncronous Text Writer for " + 375 config.dn().toNormalizedString(), config.getQueueSize(), 376 config.isAutoFlush(), 377 mfWriter); 378 writer = asyncWriter; 379 } 380 381 if((currentConfig.isAsynchronous() && config.isAsynchronous()) && 382 (currentConfig.getQueueSize() != config.getQueueSize())) 383 { 384 adminActionRequired = true; 385 } 386 387 currentConfig = config; 388 } 389 } 390 catch(Exception e) 391 { 392 Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get( 393 config.dn().toString(), 394 stackTraceToSingleLineString(e)); 395 resultCode = DirectoryServer.getServerErrorResultCode(); 396 messages.add(message); 397 398 } 399 400 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 401 } 402 403 /** 404 * {@inheritDoc} 405 */ 406 public boolean isConfigurationAddAcceptable(DebugTargetCfg config, 407 List<Message> unacceptableReasons) 408 { 409 return getTraceSettings(config.getDebugScope()) == null; 410 } 411 412 /** 413 * {@inheritDoc} 414 */ 415 public boolean isConfigurationDeleteAcceptable(DebugTargetCfg config, 416 List<Message> unacceptableReasons) 417 { 418 // A delete should always be acceptable. 419 return true; 420 } 421 422 /** 423 * {@inheritDoc} 424 */ 425 public ConfigChangeResult applyConfigurationAdd(DebugTargetCfg config) 426 { 427 // Default result code. 428 ResultCode resultCode = ResultCode.SUCCESS; 429 boolean adminActionRequired = false; 430 ArrayList<Message> messages = new ArrayList<Message>(); 431 432 addTraceSettings(config.getDebugScope(), new TraceSettings(config)); 433 434 DebugLogger.updateTracerSettings(); 435 436 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 437 } 438 439 /** 440 * {@inheritDoc} 441 */ 442 public ConfigChangeResult applyConfigurationDelete(DebugTargetCfg config) 443 { 444 // Default result code. 445 ResultCode resultCode = ResultCode.SUCCESS; 446 boolean adminActionRequired = false; 447 ArrayList<Message> messages = new ArrayList<Message>(); 448 449 removeTraceSettings(config.getDebugScope()); 450 451 DebugLogger.updateTracerSettings(); 452 453 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 454 } 455 456 /** 457 * {@inheritDoc} 458 */ 459 public void traceConstructor(LogLevel level, 460 TraceSettings settings, 461 String signature, 462 String sourceLocation, 463 Object[] args, 464 StackTraceElement[] stackTrace) 465 { 466 LogCategory category = DebugLogCategory.CONSTRUCTOR; 467 468 String msg = ""; 469 if(args != null) 470 { 471 msg = buildDefaultEntryMessage(args); 472 } 473 474 String stack = null; 475 if(stackTrace != null) 476 { 477 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 478 settings.stackDepth); 479 } 480 publish(category, level, signature, sourceLocation, msg, stack); 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 public void traceMethodEntry(LogLevel level, 487 TraceSettings settings, 488 String signature, 489 String sourceLocation, 490 Object obj, 491 Object[] args, 492 StackTraceElement[] stackTrace) 493 { 494 LogCategory category = DebugLogCategory.ENTER; 495 String msg = ""; 496 if(args != null) 497 { 498 msg = buildDefaultEntryMessage(args); 499 } 500 501 String stack = null; 502 if(stackTrace != null) 503 { 504 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 505 settings.stackDepth); 506 } 507 publish(category, level, signature, sourceLocation, msg, stack); 508 } 509 510 /** 511 * {@inheritDoc} 512 */ 513 public void traceStaticMethodEntry(LogLevel level, 514 TraceSettings settings, 515 String signature, 516 String sourceLocation, 517 Object[] args, 518 StackTraceElement[] stackTrace) 519 { 520 LogCategory category = DebugLogCategory.ENTER; 521 String msg = ""; 522 if(args != null) 523 { 524 msg = buildDefaultEntryMessage(args); 525 } 526 527 String stack = null; 528 if(stackTrace != null) 529 { 530 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 531 settings.stackDepth); 532 } 533 publish(category, level, signature, sourceLocation, msg, stack); 534 } 535 536 /** 537 * {@inheritDoc} 538 */ 539 public void traceReturn(LogLevel level, 540 TraceSettings settings, 541 String signature, 542 String sourceLocation, 543 Object ret, 544 StackTraceElement[] stackTrace) 545 { 546 LogCategory category = DebugLogCategory.EXIT; 547 String msg = ""; 548 if(ret != null) 549 { 550 msg = DebugMessageFormatter.format("returned={%s}", 551 new Object[] {ret}); 552 } 553 554 String stack = null; 555 if(stackTrace != null) 556 { 557 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 558 settings.stackDepth); 559 } 560 publish(category, level, signature, sourceLocation, msg, stack); 561 } 562 563 /** 564 * {@inheritDoc} 565 */ 566 public void traceThrown(LogLevel level, 567 TraceSettings settings, 568 String signature, 569 String sourceLocation, 570 Throwable ex, 571 StackTraceElement[] stackTrace) 572 { 573 LogCategory category = DebugLogCategory.THROWN; 574 575 String msg = DebugMessageFormatter.format("thrown={%s}", 576 new Object[] {ex}); 577 578 String stack = null; 579 if(stackTrace != null) 580 { 581 stack = DebugStackTraceFormatter.formatStackTrace(ex, 582 settings.stackDepth, 583 settings.includeCause); 584 } 585 publish(category, level, signature, sourceLocation, msg, stack); 586 } 587 588 /** 589 * {@inheritDoc} 590 */ 591 public void traceMessage(LogLevel level, 592 TraceSettings settings, 593 String signature, 594 String sourceLocation, 595 String msg, 596 StackTraceElement[] stackTrace) 597 { 598 LogCategory category = DebugLogCategory.MESSAGE; 599 600 String stack = null; 601 if(stackTrace != null) 602 { 603 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 604 settings.stackDepth); 605 } 606 publish(category, level, signature, sourceLocation, msg, stack); 607 } 608 609 /** 610 * {@inheritDoc} 611 */ 612 public void traceCaught(LogLevel level, 613 TraceSettings settings, 614 String signature, 615 String sourceLocation, 616 Throwable ex, 617 StackTraceElement[] stackTrace) 618 { 619 LogCategory category = DebugLogCategory.CAUGHT; 620 String msg = DebugMessageFormatter.format("caught={%s}", 621 new Object[] {ex}); 622 623 String stack = null; 624 if(stackTrace != null) 625 { 626 stack = DebugStackTraceFormatter.formatStackTrace(ex, 627 settings.stackDepth, 628 settings.includeCause); 629 } 630 publish(category, level, signature, sourceLocation, msg, stack); 631 } 632 633 /** 634 * {@inheritDoc} 635 */ 636 public void traceJEAccess(LogLevel level, 637 TraceSettings settings, 638 String signature, 639 String sourceLocation, 640 OperationStatus status, 641 Database database, Transaction txn, 642 DatabaseEntry key, DatabaseEntry data, 643 StackTraceElement[] stackTrace) 644 { 645 LogCategory category = DebugLogCategory.DATABASE_ACCESS; 646 647 // Build the string that is common to category DATABASE_ACCESS. 648 StringBuilder builder = new StringBuilder(); 649 builder.append(" ("); 650 builder.append(status.toString()); 651 builder.append(")"); 652 builder.append(" db="); 653 try 654 { 655 builder.append(database.getDatabaseName()); 656 } 657 catch(DatabaseException de) 658 { 659 builder.append(de.toString()); 660 } 661 if (txn != null) 662 { 663 builder.append(" txnid="); 664 try 665 { 666 builder.append(txn.getId()); 667 } 668 catch(DatabaseException de) 669 { 670 builder.append(de.toString()); 671 } 672 } 673 else 674 { 675 builder.append(" txnid=none"); 676 } 677 678 builder.append(ServerConstants.EOL); 679 if(key != null) 680 { 681 builder.append("key:"); 682 builder.append(ServerConstants.EOL); 683 StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); 684 } 685 686 // If the operation was successful we log the same common information 687 // plus the data 688 if (status == OperationStatus.SUCCESS && data != null) 689 { 690 691 builder.append("data(len="); 692 builder.append(data.getSize()); 693 builder.append("):"); 694 builder.append(ServerConstants.EOL); 695 StaticUtils.byteArrayToHexPlusAscii(builder, data.getData(), 4); 696 697 } 698 699 String stack = null; 700 if(stackTrace != null) 701 { 702 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 703 settings.stackDepth); 704 } 705 publish(category, level, signature, sourceLocation, 706 builder.toString(), stack); 707 } 708 709 /** 710 * {@inheritDoc} 711 */ 712 public void traceData(LogLevel level, 713 TraceSettings settings, 714 String signature, 715 String sourceLocation, 716 byte[] data, 717 StackTraceElement[] stackTrace) 718 { 719 LogCategory category = DebugLogCategory.DATA; 720 if(data != null) 721 { 722 StringBuilder builder = new StringBuilder(); 723 builder.append(ServerConstants.EOL); 724 builder.append("data(len="); 725 builder.append(data.length); 726 builder.append("):"); 727 builder.append(ServerConstants.EOL); 728 StaticUtils.byteArrayToHexPlusAscii(builder, data, 4); 729 730 String stack = null; 731 if(stackTrace != null) 732 { 733 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 734 settings.stackDepth); 735 } 736 publish(category, level, signature, sourceLocation, 737 builder.toString(), stack); 738 } 739 } 740 741 /** 742 * {@inheritDoc} 743 */ 744 public void traceProtocolElement(LogLevel level, 745 TraceSettings settings, 746 String signature, 747 String sourceLocation, 748 ProtocolElement element, 749 StackTraceElement[] stackTrace) 750 { 751 LogCategory category = DebugLogCategory.PROTOCOL; 752 753 StringBuilder builder = new StringBuilder(); 754 builder.append(ServerConstants.EOL); 755 element.toString(builder, 4); 756 757 String stack = null; 758 if(stackTrace != null) 759 { 760 stack = DebugStackTraceFormatter.formatStackTrace(stackTrace, 761 settings.stackDepth); 762 } 763 publish(category, level, signature, sourceLocation, 764 builder.toString(), stack); 765 } 766 767 /** 768 * {@inheritDoc} 769 */ 770 public void close() 771 { 772 writer.shutdown(); 773 774 if(currentConfig != null) 775 { 776 currentConfig.removeFileBasedDebugChangeListener(this); 777 } 778 } 779 780 781 // Publishes a record, optionally performing some "special" work: 782 // - injecting a stack trace into the message 783 // - format the message with argument values 784 private void publish(LogCategory category, LogLevel level, String signature, 785 String sourceLocation, String msg, String stack) 786 { 787 Thread thread = Thread.currentThread(); 788 789 StringBuilder buf = new StringBuilder(); 790 // Emit the timestamp. 791 buf.append("["); 792 buf.append(TimeThread.getLocalTime()); 793 buf.append("] "); 794 795 // Emit the seq num 796 buf.append(globalSequenceNumber++); 797 buf.append(" "); 798 799 // Emit debug category. 800 buf.append(category); 801 buf.append(" "); 802 803 // Emit the debug level. 804 buf.append(level); 805 buf.append(" "); 806 807 // Emit thread info. 808 buf.append("thread={"); 809 buf.append(thread.getName()); 810 buf.append("("); 811 buf.append(thread.getId()); 812 buf.append(")} "); 813 814 if(thread instanceof DirectoryThread) 815 { 816 buf.append("threadDetail={"); 817 for (Map.Entry<String, String> entry : 818 ((DirectoryThread) thread).getDebugProperties().entrySet()) 819 { 820 buf.append(entry.getKey()); 821 buf.append("="); 822 buf.append(entry.getValue()); 823 buf.append(" "); 824 } 825 buf.append("} "); 826 } 827 828 // Emit method info. 829 buf.append("method={"); 830 buf.append(signature); 831 buf.append("("); 832 buf.append(sourceLocation); 833 buf.append(")} "); 834 835 // Emit message. 836 buf.append(msg); 837 838 // Emit Stack Trace. 839 if(stack != null) 840 { 841 buf.append("\nStack Trace:\n"); 842 buf.append(stack); 843 } 844 845 writer.writeRecord(buf.toString()); 846 } 847 848 private String buildDefaultEntryMessage(Object[] args) 849 { 850 StringBuilder format = new StringBuilder(); 851 for (int i = 0; i < args.length; i++) 852 { 853 if (i != 0) format.append(", "); 854 format.append("arg"); 855 format.append(i + 1); 856 format.append("={%s}"); 857 } 858 859 return DebugMessageFormatter.format(format.toString(), args); 860 } 861 862 /** 863 * {@inheritDoc} 864 */ 865 public DN getDN() 866 { 867 if(currentConfig != null) 868 { 869 return currentConfig.dn(); 870 } 871 else 872 { 873 return null; 874 } 875 } 876 }