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 /** 034 * Where do the user want its log written to ? 035 */ 036 enum Stream { 037 OUT { 038 @Override 039 PrintStream getStream(final LogEntry entry) { 040 return System.out; 041 } 042 }, 043 ERR { 044 @Override 045 PrintStream getStream(final LogEntry entry) { 046 return System.err; 047 } 048 }, 049 AUTO { 050 @Override 051 PrintStream getStream(final LogEntry entry) { 052 PrintStream stream = System.out; 053 if (entry.getLevel().compareTo(LogLevel.INFO) > 0) { 054 stream = System.err; 055 } 056 return stream; 057 } 058 }; 059 060 /** 061 * Returns the appropriate stream to write the entry to. 062 */ 063 abstract PrintStream getStream(LogEntry entry); 064 } 065 066 /** The level of log entries to display in the console (default: {@link LogLevel#INFO INFO}). */ 067 private LogLevel level = LogLevel.INFO; 068 069 /** Specify which PrintStream to use when printing a log statement. */ 070 private Stream stream = Stream.ERR; 071 072 /** 073 * Sets the level of log entries to display in the console. 074 * @param level level of log entries to display in the console 075 */ 076 public void setLevel(final LogLevel level) { 077 this.level = level; 078 } 079 080 /** 081 * Sets the stream to write entries to. 082 * @param stream the stream to write entries to. 083 */ 084 public void setStream(final Stream stream) { 085 this.stream = stream; 086 } 087 088 @Override 089 public void log(LogEntry entry) { 090 if (isLoggable(entry.getSource(), entry.getLevel())) { 091 synchronized (this) { 092 PrintStream stream = this.stream.getStream(entry); 093 writeEntry(stream, entry); 094 if ("throwable".equals(entry.getType()) && (entry.getData() instanceof Throwable)) { 095 Throwable throwable = (Throwable) entry.getData(); 096 writeShortThrowable(stream, throwable); 097 if (level.compareTo(LogLevel.DEBUG) <= 0) { 098 writeStackTrace(stream, throwable); 099 } 100 } 101 writeSeparator(stream); 102 stream.flush(); 103 } 104 } 105 } 106 107 private void writeSeparator(final PrintStream stream) { 108 for (int i = 0; i < 30; i++) { 109 stream.print('-'); 110 } 111 stream.println(); 112 } 113 114 private void writeEntry(final PrintStream stream, final LogEntry entry) { 115 writeHeader(stream, entry.getTime(), entry.getLevel(), entry.getSource()); 116 writeMessage(stream, entry.getMessage()); 117 } 118 119 private void writeShortThrowable(final PrintStream stream, final Throwable throwable) { 120 // Print each of the chained exception's messages (in order) 121 Throwable current = throwable; 122 while (current != null) { 123 writeMessage(stream, format("[%25s] > %s", 124 current.getClass().getSimpleName(), 125 current.getLocalizedMessage())); 126 current = current.getCause(); 127 } 128 } 129 130 private void writeStackTrace(final PrintStream stream, final Throwable throwable) { 131 stream.println(); 132 throwable.printStackTrace(stream); 133 } 134 135 private void writeHeader(final PrintStream stream, final long time, final LogLevel level, final Name name) { 136 // Example: "Sun Jul 20 16:17:00 EDT 1969 (INFO) " 137 stream.printf("%Tc (%s) %s%n", 138 time, 139 level.name(), 140 name.getLeaf()); 141 } 142 143 private void writeMessage(final PrintStream stream, final String message) { 144 stream.println(message); 145 } 146 147 @Override 148 public boolean isLoggable(Name source, LogLevel level) { 149 return (level.compareTo(this.level) >= 0); 150 } 151 152 /** 153 * Creates and initializes a console sink in a heap environment. 154 */ 155 public static class Heaplet extends GenericHeaplet { 156 @Override 157 public Object create() throws HeapException { 158 ConsoleLogSink sink = new ConsoleLogSink(); 159 sink.setLevel(config.get("level").defaultTo(sink.level.toString()).asEnum(LogLevel.class)); 160 sink.setStream(config.get("stream").defaultTo(sink.stream.toString()).asEnum(Stream.class)); 161 return sink; 162 } 163 } 164}