001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010-2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2014 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.log; 019 020import static java.lang.String.*; 021 022import java.io.PrintStream; 023 024import org.forgerock.openig.heap.GenericHeaplet; 025import org.forgerock.openig.heap.HeapException; 026import org.forgerock.openig.heap.Name; 027 028/** 029 * A sink that writes log entries to the standard output or error stream (depending on the object configuration). 030 */ 031public class ConsoleLogSink implements LogSink { 032 033 private static final Object LOCK = new Object(); 034 035 /** 036 * Where do the user want its log written to ? 037 */ 038 enum Stream { 039 OUT { 040 @Override 041 PrintStream getStream(final LogEntry entry) { 042 return System.out; 043 } 044 }, 045 ERR { 046 @Override 047 PrintStream getStream(final LogEntry entry) { 048 return System.err; 049 } 050 }, 051 AUTO { 052 @Override 053 PrintStream getStream(final LogEntry entry) { 054 PrintStream stream = System.out; 055 if (entry.getLevel().compareTo(LogLevel.INFO) > 0) { 056 stream = System.err; 057 } 058 return stream; 059 } 060 }; 061 062 /** 063 * Returns the appropriate stream to write the entry to. 064 */ 065 abstract PrintStream getStream(LogEntry entry); 066 } 067 068 /** The level of log entries to display in the console (default: {@link LogLevel#INFO INFO}). */ 069 private LogLevel level = LogLevel.INFO; 070 071 /** Specify which PrintStream to use when printing a log statement. */ 072 private Stream stream = Stream.ERR; 073 074 /** 075 * Sets the level of log entries to display in the console. 076 * @param level level of log entries to display in the console 077 */ 078 public void setLevel(final LogLevel level) { 079 this.level = level; 080 } 081 082 /** 083 * Sets the stream to write entries to. 084 * @param stream the stream to write entries to. 085 */ 086 public void setStream(final Stream stream) { 087 this.stream = stream; 088 } 089 090 @Override 091 public void log(LogEntry entry) { 092 if (isLoggable(entry.getSource(), entry.getLevel())) { 093 synchronized (LOCK) { 094 PrintStream stream = this.stream.getStream(entry); 095 writeEntry(stream, entry); 096 if ("throwable".equals(entry.getType()) && (entry.getData() instanceof Throwable)) { 097 Throwable throwable = (Throwable) entry.getData(); 098 writeShortThrowable(stream, throwable); 099 if (level.compareTo(LogLevel.DEBUG) <= 0) { 100 writeStackTrace(stream, throwable); 101 } 102 } 103 writeSeparator(stream); 104 stream.flush(); 105 } 106 } 107 } 108 109 private void writeSeparator(final PrintStream stream) { 110 for (int i = 0; i < 30; i++) { 111 stream.print('-'); 112 } 113 stream.println(); 114 } 115 116 private void writeEntry(final PrintStream stream, final LogEntry entry) { 117 writeHeader(stream, entry.getTime(), entry.getLevel(), entry.getSource()); 118 writeMessage(stream, entry.getMessage()); 119 } 120 121 private void writeShortThrowable(final PrintStream stream, final Throwable throwable) { 122 // Print each of the chained exception's messages (in order) 123 Throwable current = throwable; 124 while (current != null) { 125 writeMessage(stream, format("[%25s] > %s", 126 current.getClass().getSimpleName(), 127 current.getLocalizedMessage())); 128 current = current.getCause(); 129 } 130 } 131 132 private void writeStackTrace(final PrintStream stream, final Throwable throwable) { 133 stream.println(); 134 throwable.printStackTrace(stream); 135 } 136 137 private void writeHeader(final PrintStream stream, final long time, final LogLevel level, final Name name) { 138 // Example: "Sun Jul 20 16:17:00 EDT 1969 (INFO) " 139 stream.printf("%Tc (%s) %s%n", 140 time, 141 level.name(), 142 name.getLeaf()); 143 } 144 145 private void writeMessage(final PrintStream stream, final String message) { 146 stream.println(message); 147 } 148 149 @Override 150 public boolean isLoggable(Name source, LogLevel level) { 151 return (level.compareTo(this.level) >= 0); 152 } 153 154 /** 155 * Creates and initializes a console sink in a heap environment. 156 */ 157 public static class Heaplet extends GenericHeaplet { 158 @Override 159 public Object create() throws HeapException { 160 ConsoleLogSink sink = new ConsoleLogSink(); 161 sink.setLevel(config.get("level").defaultTo(sink.level.toString()).asEnum(LogLevel.class)); 162 sink.setStream(config.get("stream").defaultTo(sink.stream.toString()).asEnum(Stream.class)); 163 return sink; 164 } 165 } 166}