1 package org.grlea.log; 2 3 6 18 import org.grlea.log.rollover.RolloverManager; 19 20 import java.io.File ; 21 import java.io.FileWriter ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.OutputStreamWriter ; 25 import java.io.PrintWriter ; 26 import java.io.StringWriter ; 27 import java.io.Writer ; 28 import java.io.UnsupportedEncodingException ; 29 import java.lang.ref.Reference ; 30 import java.lang.ref.ReferenceQueue ; 31 import java.lang.ref.WeakReference ; 32 import java.net.MalformedURLException ; 33 import java.net.URI ; 34 import java.net.URISyntaxException ; 35 import java.net.URL ; 36 import java.net.URLConnection ; 37 import java.net.URLEncoder ; 38 import java.text.DateFormat ; 39 import java.text.FieldPosition ; 40 import java.text.Format ; 41 import java.text.MessageFormat ; 42 import java.text.ParsePosition ; 43 import java.text.SimpleDateFormat ; 44 import java.util.ArrayList ; 45 import java.util.Date ; 46 import java.util.Enumeration ; 47 import java.util.Iterator ; 48 import java.util.List ; 49 import java.util.Properties ; 50 import java.util.Timer ; 51 import java.util.TimerTask ; 52 import java.util.Set ; 53 import java.util.Map ; 54 55 66 public final class 67 SimpleLog 68 { 69 71 74 75 private static final String CONFIG_PROPERTY = "simplelog.configuration"; 76 77 78 private static final String DEV_DEBUG_PROPERTY = "simplelog.dev.debug"; 79 80 81 private static final String DEV_STACKTRACES_PROPERTY = "simplelog.dev.printStackTraces"; 82 83 84 private static final String FILE_CONFIG_PREFIX = "file:"; 85 86 87 private static final String CLASSPATH_CONFIG_PREFIX = "classpath:"; 88 89 90 private static final String DEFAULT_PROPERTIES_FILE_NAME = "simplelog.properties"; 91 92 93 private static final String KEY_PREFIX = "simplelog."; 94 95 96 private static final String KEY_IMPORT = KEY_PREFIX + "import"; 97 98 99 private static final String KEY_FORMAT_PREFIX = KEY_PREFIX + "format."; 100 101 102 private static final String KEY_FORMAT_INSTANCE_SUFFIX = ".instance"; 103 104 105 private static final String KEY_FORMAT_DB = KEY_FORMAT_PREFIX + "debug"; 106 107 108 private static final String KEY_FORMAT_DBO = KEY_FORMAT_PREFIX + "debugObject"; 109 110 111 private static final String KEY_FORMAT_DBE = KEY_FORMAT_PREFIX + "debugException"; 112 113 114 private static final String KEY_FORMAT_ENTRY = KEY_FORMAT_PREFIX + "entry"; 115 116 117 private static final String KEY_FORMAT_EXIT = KEY_FORMAT_PREFIX + "exit"; 118 119 120 private static final String KEY_RELOADING = KEY_PREFIX + "reloading"; 121 122 123 private static final String RELOADING_DEFAULT = "false"; 124 125 126 private static final String KEY_LOG_FILE = KEY_PREFIX + "logFile"; 127 128 129 private static final String KEY_INTERPRET_NAME = KEY_LOG_FILE + ".interpretName"; 130 131 132 private static final boolean INTREPRET_NAME_DEFAULT = true; 133 134 135 private static final String KEY_APPEND = KEY_LOG_FILE + ".append"; 136 137 138 private static final boolean APPEND_DEFAULT = true; 139 140 141 private static final String KEY_PIPE_TO_CONSOLE = KEY_LOG_FILE + ".andConsole"; 142 143 144 private static final boolean PIPE_TO_CONSOLE_DEFAULT = false; 145 146 147 private static final String KEY_DEFAULT_LEVEL = KEY_PREFIX + "defaultLevel"; 148 149 150 private static final String KEY_DEFAULT_TRACE = KEY_PREFIX + "defaultTrace"; 151 152 153 private static final String KEY_DATE_FORMAT = KEY_PREFIX + "dateFormat"; 154 155 156 private static final String DATE_FORMAT_DEFAULT = "EEE yyyy/MM/dd HH:mm:ss.SSS"; 157 158 159 private static final String KEY_PRINT_STACK_TRACES = KEY_PREFIX + "printStackTraces"; 160 161 162 private static final String PRINT_STACK_TRACES_DEFAULT = "true"; 163 164 165 private static final String KEY_ROLLOVER_STRATEGY = "simplelog.rollover"; 166 167 171 private static final int RELOAD_FILE_CHECK_PERIOD = 20 * 1000; 172 173 176 private static final int RELOAD_URL_CHECK_PERIOD = 60 * 1000; 177 178 181 private static final String TRACE_SUFFIX = "#trace"; 182 183 186 private static final String LINE_SEP = System.getProperty("line.separator"); 187 188 189 private static final String ROLLOVER_WRITER_CLASS = "org.grlea.log.rollover.RolloverManager"; 190 191 200 private static final String DEFAULT_FORMAT_STRING_DB = "{0}| |{1}|{2}|{5}"; 201 private static final String DEFAULT_FORMAT_STRING_DBO = "{0}|---|{1}|{2}|{5}|{6}"; 202 private static final String DEFAULT_FORMAT_STRING_DBE = "{0}|***|{1}|{2}|{5}"; 203 private static final String DEFAULT_FORMAT_STRING_ENTRY = "{0}|>>>|{1}|{2}|{5}"; 204 private static final String DEFAULT_FORMAT_STRING_EXIT = "{0}|<<<|{1}|{2}|{5}"; 205 206 private static final String DEFAULT_FORMAT_STRING_DB_INSTANCE = "{0}| |{1}|{2}[{3}]|{5}"; 207 private static final String DEFAULT_FORMAT_STRING_DBO_INSTANCE = "{0}|---|{1}|{2}[{3}]|{5}|{6}"; 208 private static final String DEFAULT_FORMAT_STRING_DBE_INSTANCE = "{0}|***|{1}|{2}[{3}]|{5}"; 209 private static final String DEFAULT_FORMAT_STRING_ENTRY_INSTANCE = "{0}|>>>|{1}|{2}[{3}]|{5}"; 210 private static final String DEFAULT_FORMAT_STRING_EXIT_INSTANCE = "{0}|<<<|{1}|{2}[{3}]|{5}"; 211 212 private static final int DEFAULT_FORMAT_EXCEPTION_INDEX = 5; 213 private static final int DEFAULT_FORMAT_EXCEPTION_INDEX_INSTANCE = 5; 214 215 218 223 private static SimpleLog defaultInstance = null; 224 225 228 private static final Object defaultInstanceLock = new Object (); 229 230 233 private static boolean devDebug = false; 234 235 static 236 { 237 try 238 { 239 String devDebugString = System.getProperty(DEV_DEBUG_PROPERTY); 240 if (devDebugString != null && "true".equalsIgnoreCase(devDebugString.trim())) 241 { 242 devDebug = true; 243 printDebugIfEnabled("Simple Log DEV Debugging enabled (-Dsimplelog.dev.debug)"); 244 } 245 } 246 catch (Exception e) 247 { 248 printError("Exception while reading system property '" + DEV_DEBUG_PROPERTY + "'", e, true); 249 } 250 } 251 252 255 258 private final URL configurationSource; 259 260 261 private final Properties properties; 262 263 264 private PrintWriter out; 265 266 267 private Writer currentWriter; 268 269 270 private boolean outputSetProgramatically = false; 271 272 276 private String logFile; 277 278 279 private boolean printWriterGoesToConsole = true; 280 281 282 private boolean pipingOutputToConsole = false; 283 284 285 private DebugLevel defaultLevel = DebugLevel.L4_INFO; 286 287 288 private boolean defaultTracing = false; 289 290 293 private final List loggers = new ArrayList (32); 294 295 299 private final List instanceLoggerReferences = new ArrayList (16); 300 301 302 private ReferenceQueue instanceLoggersReferenceQueue = null; 303 304 305 private final Object LOGGERS_LOCK = new Object (); 306 307 308 private DateFormat dateFormat; 309 310 314 private MessageFormat dbFormat; 315 316 321 private MessageFormat dboFormat; 322 323 327 private MessageFormat dbeFormat; 328 329 333 private MessageFormat entryFormat; 334 335 339 private MessageFormat exitFormat; 340 341 345 private MessageFormat dbFormat4Instance; 346 347 352 private MessageFormat dboFormat4Instance; 353 354 358 private MessageFormat dbeFormat4Instance; 359 360 364 private MessageFormat entryFormat4Instance; 365 366 370 private MessageFormat exitFormat4Instance; 371 372 381 public 382 SimpleLog(Properties properties) 383 { 384 if (properties == null) 385 { 386 throw new IllegalArgumentException ("properties cannot be null."); 387 } 388 389 this.configurationSource = null; 390 this.properties = properties; 391 this.out = new PrintWriter (System.err, true); 392 393 readSettingsFromProperties(); 394 } 395 396 406 public 407 SimpleLog(URL configurationSource) 408 throws IOException 409 { 410 this.configurationSource = configurationSource; 411 this.properties = new Properties (); 412 this.out = new PrintWriter (System.err, true); 413 414 loadProperties(); 415 readSettingsFromProperties(); 416 417 String reloadingString = properties.getProperty(KEY_RELOADING, RELOADING_DEFAULT); 419 boolean reloading = Boolean.valueOf(reloadingString).booleanValue(); 420 if (reloading) 421 { 422 printDebugIfEnabled("Configuration reloading enabled"); 423 424 Timer timer = new Timer (true); 425 TimerTask reloadTask; 426 int reloadPeriod; 427 428 if (configurationSource.getProtocol() != null && 429 "file".equals(configurationSource.getProtocol().toLowerCase())) 430 { 431 reloadTask = new FileConfigurationReloader(); 432 reloadPeriod = RELOAD_FILE_CHECK_PERIOD; 433 } 434 else 435 { 436 reloadTask = new UrlConfigurationReloader(); 437 reloadPeriod = RELOAD_URL_CHECK_PERIOD; 438 } 439 440 timer.schedule(reloadTask, reloadPeriod, reloadPeriod); 441 } 442 else 443 { 444 printDebugIfEnabled("Configuration reloading is disabled"); 445 } 446 } 447 448 455 private void 456 loadProperties() 457 throws IOException 458 { 459 if (properties == null) 460 { 461 return; 462 } 463 464 printDebugIfEnabled("Loading properties"); 465 466 InputStream inputStream = configurationSource.openStream(); 468 Properties newProperties = new Properties (); 469 try 470 { 471 newProperties.load(inputStream); 472 } 473 finally 474 { 475 inputStream.close(); 476 } 477 478 String importList = newProperties.getProperty(KEY_IMPORT); 480 if (importList != null) 481 { 482 importList = importList.trim(); 483 if (importList.length() > 0) 484 { 485 String [] filesToImport = importList.split(","); 486 if (filesToImport != null && filesToImport.length != 0) 487 { 488 String configurationContext = configurationSource.toExternalForm(); 489 int lastSlash = configurationContext.lastIndexOf('/'); 490 lastSlash += 1; 491 configurationContext = configurationContext.substring(0, lastSlash); 492 for (int i = 0; i < filesToImport.length; i++) 493 { 494 String filenameToImport = filesToImport[i]; 495 URL urlToImport = new URL (configurationContext + filenameToImport); 496 InputStream importStream = null; 497 try 498 { 499 printDebugIfEnabled("Importing file", urlToImport); 500 importStream = urlToImport.openStream(); 501 newProperties.load(importStream); 502 } 503 catch (IOException e) 504 { 505 printError("Error importing properties file: " + filenameToImport + 506 "(" + urlToImport + ")", e, true); 507 } 508 finally 509 { 510 if (importStream != null) 511 importStream.close(); 512 } 513 } 514 } 515 } 516 } 517 518 if (devDebug) 520 { 521 Set properties = newProperties.entrySet(); 522 printDebugIfEnabled("_____ Properties List START _____"); 523 for (Iterator iterator = properties.iterator(); iterator.hasNext();) 524 { 525 Map.Entry entry = (Map.Entry ) iterator.next(); 526 printDebugIfEnabled((String ) entry.getKey(), entry.getValue()); 527 } 528 printDebugIfEnabled("______ Properties List END ______"); 529 } 530 531 properties.clear(); 533 properties.putAll(newProperties); 534 } 535 536 540 private void 541 readSettingsFromProperties() 542 { 543 if (!outputSetProgramatically) 544 { 545 try 546 { 547 String rolloverStrategyString = properties.getProperty(KEY_ROLLOVER_STRATEGY); 548 boolean useRollover = 549 rolloverStrategyString != null && rolloverStrategyString.trim().length() != 0; 550 551 Writer newWriter; 552 553 if (useRollover) 554 { 555 newWriter = configureRolloverWriter(); 556 } 557 else 558 { 559 newWriter = configureFileWriter(); 560 } 561 562 if (newWriter != currentWriter) 563 { 564 out = new PrintWriter (newWriter, true); 565 566 if (currentWriter != null) 567 { 568 try 569 { 570 currentWriter.close(); 571 } 572 catch (IOException e) 573 { 574 printError("Error while closing log file", e, true); 575 } 576 } 577 578 currentWriter = newWriter; 579 } 580 } 581 catch (IOException e) 582 { 583 printError("Error opening log file for writing", e, true); 584 } 585 } 586 587 String pipeOutputToConsoleString = properties.getProperty(KEY_PIPE_TO_CONSOLE); 589 pipingOutputToConsole = PIPE_TO_CONSOLE_DEFAULT; 591 if (pipeOutputToConsoleString != null) 592 { 593 pipingOutputToConsole = pipeOutputToConsoleString.trim().equalsIgnoreCase("true"); 594 } 595 596 597 String defaultLevelStr = properties.getProperty(KEY_DEFAULT_LEVEL); 599 if (defaultLevelStr != null) 600 { 601 defaultLevelStr = defaultLevelStr.trim(); 602 603 try 604 { 605 int level = Integer.parseInt(defaultLevelStr); 607 defaultLevel = DebugLevel.fromInt(level); 608 } 609 catch (NumberFormatException e1) 610 { 611 try 613 { 614 defaultLevel = DebugLevel.fromName(defaultLevelStr); 615 } 616 catch (IllegalArgumentException e2) 617 { 618 printError("Error parsing debug level for '" + KEY_DEFAULT_LEVEL + "'", e1, true); 619 printError("Error parsing debug level for '" + KEY_DEFAULT_LEVEL + "'", e2, false); 620 } 621 } 622 } 623 624 String defaultTraceStr = properties.getProperty(KEY_DEFAULT_TRACE); 626 if (defaultTraceStr != null) 627 { 628 defaultTracing = Boolean.valueOf(defaultTraceStr).booleanValue(); 629 } 630 631 String dateFormatString = properties.getProperty(KEY_DATE_FORMAT, DATE_FORMAT_DEFAULT); 633 try 634 { 635 dateFormat = new SimpleDateFormat (dateFormatString); 636 } 637 catch (IllegalArgumentException e) 638 { 639 printError("Error parsing date format", e, false); 640 } 641 642 dbFormat = readFormat(KEY_FORMAT_DB, DEFAULT_FORMAT_STRING_DB); 644 dboFormat = readFormat(KEY_FORMAT_DBO, DEFAULT_FORMAT_STRING_DBO); 645 dbeFormat = readFormat(KEY_FORMAT_DBE, DEFAULT_FORMAT_STRING_DBE); 646 entryFormat = readFormat(KEY_FORMAT_ENTRY, DEFAULT_FORMAT_STRING_ENTRY); 647 exitFormat = readFormat(KEY_FORMAT_EXIT, DEFAULT_FORMAT_STRING_EXIT); 648 649 dbFormat4Instance = 650 readFormat(KEY_FORMAT_DB + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DB_INSTANCE); 651 dboFormat4Instance = 652 readFormat(KEY_FORMAT_DBO + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DBO_INSTANCE); 653 dbeFormat4Instance = 654 readFormat(KEY_FORMAT_DBE + KEY_FORMAT_INSTANCE_SUFFIX, DEFAULT_FORMAT_STRING_DBE_INSTANCE); 655 entryFormat4Instance = 656 readFormat(KEY_FORMAT_ENTRY + KEY_FORMAT_INSTANCE_SUFFIX, 657 DEFAULT_FORMAT_STRING_ENTRY_INSTANCE); 658 exitFormat4Instance = 659 readFormat(KEY_FORMAT_EXIT + KEY_FORMAT_INSTANCE_SUFFIX, 660 DEFAULT_FORMAT_STRING_EXIT_INSTANCE); 661 662 updateDateFormats(); 663 664 String printStackTracesStr = 666 properties.getProperty(KEY_PRINT_STACK_TRACES, PRINT_STACK_TRACES_DEFAULT); 667 boolean printStackTraces = Boolean.valueOf(printStackTracesStr).booleanValue(); 668 if (printStackTraces) 669 { 670 ExceptionFormat exceptionFormat = new ExceptionFormat(); 671 dbeFormat.setFormatByArgumentIndex(DEFAULT_FORMAT_EXCEPTION_INDEX, exceptionFormat); 672 dbeFormat4Instance.setFormatByArgumentIndex( 673 DEFAULT_FORMAT_EXCEPTION_INDEX_INSTANCE, exceptionFormat); 674 } 675 676 Enumeration propertyNames = properties.propertyNames(); 678 Properties newProperties = new Properties (); 679 while (propertyNames.hasMoreElements()) 680 { 681 String key = (String ) propertyNames.nextElement(); 682 if (key.indexOf('$') != -1) 683 { 684 newProperties.put(key.replace('$', '.'), properties.getProperty(key)); 685 } 686 } 687 properties.putAll(newProperties); 688 } 689 690 696 private Writer 697 configureFileWriter() 698 throws IOException 699 { 700 Writer writer; 701 String newLogFile = properties.getProperty(KEY_LOG_FILE); 703 704 boolean interpretName = INTREPRET_NAME_DEFAULT; 705 String interpretNameStr = properties.getProperty(KEY_INTERPRET_NAME); 706 if (interpretNameStr != null) 708 { 709 interpretName = !(interpretNameStr.trim().equalsIgnoreCase("false")); 710 } 711 712 boolean newLogFileNotNull = newLogFile != null; 714 boolean nameContainsBraces = newLogFileNotNull && newLogFile.indexOf('{') != -1; 715 if (newLogFileNotNull && nameContainsBraces && interpretName) 716 { 717 try 718 { 719 MessageFormat logFileNameFormat = new MessageFormat (newLogFile); 720 newLogFile = logFileNameFormat.format(new Object [] {new Date ()}); 721 } 722 catch (Exception e) 723 { 724 printError("Error generating log file name", e, true); 725 newLogFile = null; 726 } 727 } 728 729 boolean logFileChanged = (logFile == null) != (newLogFile == null); 731 732 logFileChanged |= logFile != null && newLogFileNotNull && !newLogFile.equals(logFile); 734 735 boolean rolloverWasInUse = 737 currentWriter != null && 738 currentWriter.getClass().getName().equals(ROLLOVER_WRITER_CLASS); 739 740 logFileChanged |= rolloverWasInUse; 741 742 if (logFileChanged) 743 { 744 if (newLogFile == null) 745 { 746 writer = new OutputStreamWriter (System.err); 747 printWriterGoesToConsole = true; 748 } 749 else 750 { 751 File file = new File (newLogFile).getAbsoluteFile(); 752 753 if (file.isDirectory()) 754 { 755 throw new IOException ( 756 "The specified log file name already exists as a directory."); 757 } 758 759 File parentFile = file.getParentFile(); 760 if (parentFile != null) 761 { 762 parentFile.mkdirs(); 763 } 764 765 boolean append = APPEND_DEFAULT; 766 String appendStr = properties.getProperty(KEY_APPEND); 767 if (appendStr != null) 770 { 771 append = !(appendStr.trim().equalsIgnoreCase("false")); 772 } 773 774 writer = new FileWriter (file, append); 775 776 printWriterGoesToConsole = false; 777 } 778 779 logFile = newLogFile; 780 } 781 else 782 { 783 writer = currentWriter; 784 } 785 786 return writer; 787 } 788 789 795 private Writer 796 configureRolloverWriter() 797 throws IOException 798 { 799 try 801 { 802 Class.forName(ROLLOVER_WRITER_CLASS); 803 } 804 catch (ClassNotFoundException e) 805 { 806 throw new IOException ("The RolloverManager class is not available: " + e); 807 } 808 catch (Throwable t) 809 { 810 } 812 813 Writer writer; 814 if (currentWriter != null && currentWriter.getClass().getName().equals(ROLLOVER_WRITER_CLASS)) 815 { 816 ((RolloverManager) currentWriter).configure(properties); 817 writer = currentWriter; 818 } 819 else 820 { 821 writer = RolloverManager.createRolloverManager(properties, ErrorReporter.create()); 822 } 823 824 printWriterGoesToConsole = false; 825 826 return writer; 827 } 828 829 832 public void 833 reloadProperties() 834 { 835 try 836 { 837 if (configurationSource != null) 838 { 839 loadProperties(); 840 } 841 842 readSettingsFromProperties(); 843 reconfigureAllLoggers(); 844 } 845 catch (Exception e) 846 { 847 printError("Falied to reload properties", e, true); 848 } 849 } 850 851 860 private MessageFormat 861 readFormat(String key, String defaultPattern) 862 { 863 String formatString = properties.getProperty(key, defaultPattern); 864 MessageFormat format; 865 try 866 { 867 format = new MessageFormat (formatString); 868 } 869 catch (Exception e) 870 { 871 printError("Error reading format string from " + key, e, false); 872 format = new MessageFormat (defaultPattern); 873 } 874 return format; 875 } 876 877 887 public static SimpleLog 888 defaultInstance() 889 { 890 synchronized (defaultInstanceLock) 891 { 892 if (defaultInstance == null) 893 { 894 printDebugIfEnabled("Creating default SimpleLog instance"); 895 896 URL propertiesUrl = getPropertiesUrl(); 897 898 if (propertiesUrl != null) 899 { 900 printDebugIfEnabled("Attempting to configure using properties at", propertiesUrl); 901 try 902 { 903 defaultInstance = new SimpleLog(propertiesUrl); 904 } 905 catch (Exception e) 906 { 907 printError("Error while attempting to load default properties", e, true); 908 } 909 } 910 911 if (defaultInstance == null) 912 { 913 printDebugIfEnabled(""); 914 printDebugIfEnabled("FAILED to load any SimpleLog configuration."); 915 printDebugIfEnabled(""); 916 printDebugIfEnabled("NO LOG OUTPUT WILL BE GENERATED."); 917 defaultInstance = new SimpleLog(new Properties ()); 918 defaultInstance.setWriter(null); 919 } 920 } 921 } 922 return defaultInstance; 923 } 924 925 private static URL 926 getPropertiesUrl() 927 { 928 String propertiesDefinition = null; 930 try 931 { 932 propertiesDefinition = System.getProperty(CONFIG_PROPERTY); 933 } 934 catch (SecurityException e) 935 { 936 printError("SecurityException while trying to read system property", e, true); 937 } 938 printDebugIfEnabled("System property '" + CONFIG_PROPERTY + "'", propertiesDefinition); 939 940 URL propertiesUrl = null; 941 if (propertiesDefinition != null) 942 { 943 if (propertiesDefinition.startsWith(FILE_CONFIG_PREFIX)) 945 { 946 String propertiesLocation = 947 propertiesDefinition.substring(FILE_CONFIG_PREFIX.length()); 948 949 File propertiesFile = new File (propertiesLocation); 950 951 if (propertiesFile.exists()) 952 { 953 try 954 { 955 propertiesUrl = propertiesFile.toURL(); 956 } 957 catch (MalformedURLException e) 958 { 959 printError("Error creating URL from filename '" + propertiesLocation + "'", e, 960 false); 961 } 962 } 963 else 964 { 965 printError("Properties file not found at '" + propertiesLocation + "'"); 966 } 967 } 968 else if (propertiesDefinition.startsWith(CLASSPATH_CONFIG_PREFIX)) 970 { 971 String propertiesLocation = 972 propertiesDefinition.substring(CLASSPATH_CONFIG_PREFIX.length()); 973 974 propertiesUrl = SimpleLog.class.getClassLoader().getResource(propertiesLocation); 975 if (propertiesUrl == null) 976 printError("Properties not found in classpath at '" + propertiesLocation + "'"); 977 } 978 else 980 { 981 printError(CONFIG_PROPERTY + " property must begin with '" + FILE_CONFIG_PREFIX + 982 "' or '" + CLASSPATH_CONFIG_PREFIX + "' ('" + propertiesDefinition + "')"); 983 } 984 } 985 986 if (propertiesUrl == null) 988 { 989 printDebugIfEnabled( 990 "Attempting to load default properties (simplelog.properties) from classpath"); 991 propertiesUrl = SimpleLog.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE_NAME); 992 } 993 994 if (propertiesUrl != null && propertiesUrl.toExternalForm().indexOf(' ') != -1) 996 { 997 try 998 { 999 propertiesUrl = new URL (propertiesUrl.toString().replaceAll(" ", "%20")); 1000 } 1001 catch (MalformedURLException e) 1002 { 1003 printError("Failed to encode spaces in properties URL", e, true); 1004 } 1005 } 1006 1007 return propertiesUrl; 1008 } 1009 1010 1018 void 1019 println(String s) 1020 { 1021 if (out != null) 1022 { 1023 out.println(s); 1024 if (!printWriterGoesToConsole && pipingOutputToConsole) 1025 { 1026 System.err.println(s); 1027 } 1028 } 1029 } 1030 1031 1037 void 1038 configure(SimpleLogger logger) 1039 { 1040 logger.setDebugLevel(getDebugLevel(logger)); 1041 logger.setTracing(getTracingFlag(logger)); 1042 } 1043 1044 1051 private DebugLevel 1052 getDebugLevel(SimpleLogger logger) 1053 { 1054 if (properties == null) 1055 { 1056 return defaultLevel; 1057 } 1058 1059 String loggerConfigName = logger.getConfigName(); 1060 1061 int dotIndex = loggerConfigName.length(); 1062 DebugLevel debugLevel = null; 1063 do 1064 { 1065 loggerConfigName = loggerConfigName.substring(0, dotIndex); 1068 String value = properties.getProperty(loggerConfigName); 1069 if (value != null) 1070 { 1071 value = value.trim(); 1072 1073 try 1074 { 1075 int level = Integer.parseInt(value); 1077 debugLevel = DebugLevel.fromInt(level); 1078 } 1079 catch (NumberFormatException e1) 1080 { 1081 try 1083 { 1084 debugLevel = DebugLevel.fromName(value); 1085 } 1086 catch (IllegalArgumentException e2) 1087 { 1088 printError("Error parsing debug level for '" + loggerConfigName + "'", e1, true); 1089 printError("Error parsing debug level for '" + loggerConfigName + "'", e2, false); 1090 } 1091 } 1092 } 1093 1094 dotIndex = loggerConfigName.lastIndexOf('.'); 1095 } 1096 while (debugLevel == null && dotIndex != -1); 1097 1098 if (debugLevel == null) 1100 { 1101 debugLevel = defaultLevel; 1102 } 1103 1104 return debugLevel; 1105 } 1106 1107 1114 private boolean 1115 getTracingFlag(SimpleLogger logger) 1116 { 1117 if (properties == null) 1118 { 1119 return defaultTracing; 1120 } 1121 1122 String loggerConfigName = logger.getConfigName(); 1123 1124 int dotIndex = loggerConfigName.length(); 1125 boolean trace = defaultTracing; 1126 do 1127 { 1128 loggerConfigName = loggerConfigName.substring(0, dotIndex); 1129 String value = properties.getProperty(loggerConfigName + TRACE_SUFFIX); 1130 if (value != null) 1131 { 1132 trace = Boolean.valueOf(value).booleanValue(); 1133 break; 1134 } 1135 1136 dotIndex = loggerConfigName.lastIndexOf('.'); 1137 } 1138 while (dotIndex != -1); 1139 1140 return trace; 1141 } 1142 1143 1149 void 1150 register(SimpleLogger logger) 1151 { 1152 synchronized (LOGGERS_LOCK) 1153 { 1154 if (!logger.isInstanceDebugger()) 1155 { 1156 loggers.add(logger); 1157 } 1158 else 1159 { 1160 if (instanceLoggersReferenceQueue == null) 1162 { 1163 createInstanceLoggersReferenceQueue(); 1164 } 1165 instanceLoggerReferences.add(new WeakReference (logger, instanceLoggersReferenceQueue)); 1166 } 1167 } 1168 configure(logger); 1169 } 1170 1171 1175 private void 1176 createInstanceLoggersReferenceQueue() 1177 { 1178 instanceLoggersReferenceQueue = new ReferenceQueue (); 1179 Thread thread = new Thread (new Runnable () 1180 { 1181 public void 1182 run() 1183 { 1184 while (true) 1185 { 1186 try 1187 { 1188 Thread.yield(); 1189 Reference reference = instanceLoggersReferenceQueue.remove(); 1190 synchronized (LOGGERS_LOCK) 1191 { 1192 instanceLoggerReferences.remove(reference); 1193 } 1194 } 1195 catch (Throwable t) 1196 { 1197 } 1199 } 1200 } 1201 }, "SimpleLog Instance Logger Cleaner"); 1202 thread.setDaemon(true); 1203 thread.setPriority(Thread.MIN_PRIORITY); 1204 thread.start(); 1205 } 1206 1207 1210 private void 1211 reconfigureAllLoggers() 1212 { 1213 synchronized (LOGGERS_LOCK) 1214 { 1215 printDebugIfEnabled("Re-configuring all loggers"); 1216 1217 for (Iterator iter = loggers.iterator(); iter.hasNext();) 1218 { 1219 configure((SimpleLogger) iter.next()); 1220 } 1221 1222 for (Iterator iter = instanceLoggerReferences.iterator(); iter.hasNext();) 1223 { 1224 Reference loggerReference = (Reference ) iter.next(); 1225 SimpleLogger logger = (SimpleLogger) loggerReference.get(); 1226 if (logger != null) 1227 configure(logger); 1228 } 1229 } 1230 } 1231 1232 1237 private static void 1238 printDebugIfEnabled(String message) 1239 { 1240 if (devDebug) 1241 System.err.println("SimpleLog [dev.debug]: " + message); 1242 } 1243 1244 1252 private static void 1253 printDebugIfEnabled(String message, Object value) 1254 { 1255 if (devDebug) 1256 System.err.println("SimpleLog [dev.debug]: " + message + ": " + value); 1257 } 1258 1259 1264 private static void 1265 printError(String description) 1266 { 1267 printError(description, null, false); 1268 } 1269 1270 1278 private static void 1279 printError(String description, Throwable error, boolean printExceptionType) 1280 { 1281 boolean printStackTraces = false; 1282 try 1283 { 1284 String printStackTracesStr = System.getProperty(DEV_STACKTRACES_PROPERTY); 1285 printStackTraces = printStackTracesStr != null && 1286 printStackTracesStr.trim().equalsIgnoreCase("true"); 1287 } 1288 catch (SecurityException e) 1289 { 1290 } 1292 1293 synchronized (System.err) 1294 { 1295 System.err.println(); 1296 1297 System.err.print(" SimpleLog ERROR: "); 1298 System.err.print(description); 1299 1300 if (error != null) 1301 { 1302 System.err.print(": "); 1303 if (printExceptionType) 1304 { 1305 System.err.println(error); 1306 } 1307 else 1308 { 1309 System.err.println(error.getMessage()); 1310 } 1311 1312 if (printStackTraces) 1313 { 1314 try 1315 { 1316 System.err.println(); 1317 error.printStackTrace(System.err); 1318 } 1319 catch (SecurityException e) 1320 { 1321 } 1323 } 1324 1325 System.err.println(); 1326 } 1327 else 1328 { 1329 System.err.println(); 1330 System.err.println(); 1331 } 1332 } 1333 } 1334 1335 1338 public DebugLevel 1339 getDefaultLevel() 1340 { 1341 return defaultLevel; 1342 } 1343 1344 1351 public void 1352 setDefaultLevel(DebugLevel defaultLevel) 1353 { 1354 if (defaultLevel == null) 1355 { 1356 throw new IllegalArgumentException ("defaultLevel cannot be null."); 1357 } 1358 this.defaultLevel = defaultLevel; 1359 reconfigureAllLoggers(); 1360 } 1361 1362 1365 public boolean 1366 isDefaultTracing() 1367 { 1368 return defaultTracing; 1369 } 1370 1371 1377 public void 1378 setDefaultTracing(boolean defaultTracing) 1379 { 1380 this.defaultTracing = defaultTracing; 1381 reconfigureAllLoggers(); 1382 } 1383 1384 1387 public DateFormat 1388 getDateFormat() 1389 { 1390 return dateFormat; 1391 } 1392 1393 1399 public void 1400 setDateFormat(DateFormat newDateFormat) 1401 { 1402 if (newDateFormat == null) 1403 { 1404 dateFormat = new SimpleDateFormat (DATE_FORMAT_DEFAULT); 1405 } 1406 else 1407 { 1408 dateFormat = newDateFormat; 1409 } 1410 1411 updateDateFormats(); 1412 } 1413 1414 1421 public PrintWriter 1422 getWriter() 1423 { 1424 return out; 1425 } 1426 1427 1434 public void 1435 setWriter(PrintWriter out) 1436 { 1437 this.out = out; 1438 this.outputSetProgramatically = true; 1439 } 1440 1441 1444 boolean 1445 isOutputting() 1446 { 1447 return out != null; 1448 } 1449 1450 1453 private void 1454 updateDateFormats() 1455 { 1456 dbFormat.setFormatByArgumentIndex(0, dateFormat); 1457 dboFormat.setFormatByArgumentIndex(0, dateFormat); 1458 dbeFormat.setFormatByArgumentIndex(0, dateFormat); 1459 entryFormat.setFormatByArgumentIndex(0, dateFormat); 1460 exitFormat.setFormatByArgumentIndex(0, dateFormat); 1461 1462 dbFormat4Instance.setFormatByArgumentIndex(0, dateFormat); 1463 dboFormat4Instance.setFormatByArgumentIndex(0, dateFormat); 1464 dbeFormat4Instance.setFormatByArgumentIndex(0, dateFormat); 1465 entryFormat4Instance.setFormatByArgumentIndex(0, dateFormat); 1466 exitFormat4Instance.setFormatByArgumentIndex(0, dateFormat); 1467 } 1468 1469 MessageFormat 1470 getDebugFormat() 1471 { 1472 return dbFormat; 1473 } 1474 1475 MessageFormat 1476 getDebugInstanceFormat() 1477 { 1478 return dbFormat4Instance; 1479 } 1480 1481 MessageFormat 1482 getDebugObjectFormat() 1483 { 1484 return dboFormat; 1485 } 1486 1487 MessageFormat 1488 getDebugObjectInstanceFormat() 1489 { 1490 return dboFormat4Instance; 1491 } 1492 1493 MessageFormat 1494 getDebugExceptionFormat() 1495 { 1496 return dbeFormat; 1497 } 1498 1499 MessageFormat 1500 getDebugExceptionInstanceFormat() 1501 { 1502 return dbeFormat4Instance; 1503 } 1504 1505 MessageFormat 1506 getEntryFormat() 1507 { 1508 return entryFormat; 1509 } 1510 1511 MessageFormat 1512 getEntryInstanceFormat() 1513 { 1514 return entryFormat4Instance; 1515 } 1516 1517 MessageFormat 1518 getExitFormat() 1519 { 1520 return exitFormat; 1521 } 1522 1523 MessageFormat 1524 getExitInstanceFormat() 1525 { 1526 return exitFormat4Instance; 1527 } 1528 1529 1536 public boolean 1537 isPipingOutputToConsole() 1538 { 1539 return pipingOutputToConsole; 1540 } 1541 1542 1549 public void 1550 setPipingOutputToConsole(boolean pipeOutputToConsole) 1551 { 1552 this.pipingOutputToConsole = pipeOutputToConsole; 1553 } 1554 1555 1559 private static final class 1560 ExceptionFormat 1561 extends Format 1562 { 1563 public StringBuffer 1564 format(Object obj, StringBuffer buf, FieldPosition pos) 1565 { 1566 if (!(obj instanceof Throwable )) 1567 { 1568 throw new IllegalArgumentException (getClass().getName() + " only formats Throwables."); 1569 } 1570 1571 Throwable t = (Throwable ) obj; 1572 buf.append(t); 1573 1574 StringWriter sw; 1576 t.printStackTrace(new PrintWriter (sw = new StringWriter ())); 1577 buf.append(LINE_SEP).append(sw.toString()); 1578 1579 return buf; 1580 } 1581 1582 public Object 1583 parseObject (String source, ParsePosition pos) 1584 { 1585 throw new UnsupportedOperationException (); 1587 } 1588 } 1589 1590 1595 private final class 1596 FileConfigurationReloader 1597 extends TimerTask 1598 { 1599 private final File configurationFile; 1600 1601 private long previousLastModified; 1602 1603 public 1604 FileConfigurationReloader() 1605 { 1606 try 1607 { 1608 URI uri = new URI (configurationSource.toExternalForm()); 1609 this.configurationFile = new File (uri); 1610 this.previousLastModified = configurationFile.lastModified(); 1611 } 1612 catch (URISyntaxException e) 1613 { 1614 throw new IllegalArgumentException ("Failed to create URI from URL due to " + e); 1615 } 1616 } 1617 1618 public void 1619 run() 1620 { 1621 long lastModified = configurationFile.lastModified(); 1622 if (previousLastModified != lastModified) 1623 { 1624 reloadProperties(); 1625 previousLastModified = lastModified; 1626 } 1627 } 1628 } 1629 1630 1636 private final class 1637 UrlConfigurationReloader 1638 extends TimerTask 1639 { 1640 private long previousLastModified; 1641 1642 public 1643 UrlConfigurationReloader() 1644 { 1645 try 1646 { 1647 URLConnection connection = configurationSource.openConnection(); 1648 previousLastModified = connection.getLastModified(); 1649 } 1650 catch (IOException e) 1651 { 1652 previousLastModified = 0; 1653 } 1654 } 1655 1656 public void 1657 run() 1658 { 1659 long lastModified; 1660 try 1661 { 1662 URLConnection connection = configurationSource.openConnection(); 1663 lastModified = connection.getLastModified(); 1664 } 1665 catch (IOException e) 1666 { 1667 lastModified = 0; 1668 } 1669 1670 if (lastModified == 0) 1671 { 1672 Properties currentProperties = new Properties (); 1674 try 1675 { 1676 InputStream inputStream = configurationSource.openStream(); 1677 currentProperties.load(inputStream); 1678 if (!currentProperties.equals(properties)) 1679 { 1680 reloadProperties(); 1681 } 1682 } 1683 catch (IOException e) 1684 { 1685 } 1687 } 1688 else if (previousLastModified != lastModified) 1689 { 1690 reloadProperties(); 1691 previousLastModified = lastModified; 1692 } 1693 } 1694 } 1695 1696 private static class 1697 ErrorReporter 1698 implements RolloverManager.ErrorReporter 1699 { 1700 public void 1701 error(String description, Throwable t, boolean printExceptionType) 1702 { 1703 printError(description, t, printExceptionType); 1704 } 1705 1706 private static RolloverManager.ErrorReporter 1707 create() 1708 { 1709 return new ErrorReporter(); 1710 } 1711 } 1712} 1713 | Popular Tags |