All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.questdb.log.Logger Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *    ___                  _   ____  ____
 *   / _ \ _   _  ___  ___| |_|  _ \| __ )
 *  | | | | | | |/ _ \/ __| __| | | |  _ \
 *  | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *   \__\_\\__,_|\___||___/\__|____/|____/
 *
 * Copyright (C) 2014-2019 Appsicle
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 *
 ******************************************************************************/

package com.questdb.log;

import com.questdb.mp.RingQueue;
import com.questdb.mp.Sequence;
import com.questdb.network.Net;
import com.questdb.std.Misc;
import com.questdb.std.Sinkable;
import com.questdb.std.microtime.MicrosecondClock;
import com.questdb.std.str.CharSink;

import java.io.File;

/**
 * Builds and sends log messages to writer thread. Log messages are constructed using "builder" pattern,
 * which usually begins with "level method" {@link #debug()}, {@link #info()} or {@link #error()} followed by
 * $(x) to append log message content and must terminate with {@link #$()}. There are $(x) methods for all types
 * of "x" parameter to help avoid string concatenation and string creation in general.
 * 

* * private static final Log LOG = LogFactory.getLog(MyClass.class); * ... * LOG.info().$("Hello world: ").$(123).$(); * *

*

* Logger appends messages to native memory buffer and dispatches buffer to writer thread queue with {@link #$()} call. * When writer queue is full all logger method calls between level and $() become no-ops. In this case queue size * have to be increased or choice of log storage has to be reviewed. Depending on complexity of log message * structure it should be possible to log between 1,000,000 and 10,000,000 messages per second to SSD device. *

*/ class Logger implements LogRecord, Log { private final CharSequence name; private final RingQueue debugRing; private final Sequence debugSeq; private final RingQueue infoRing; private final Sequence infoSeq; private final RingQueue errorRing; private final Sequence errorSeq; private final ThreadLocalCursor tl = new ThreadLocalCursor(); private final MicrosecondClock clock; Logger( MicrosecondClock clock, CharSequence name, RingQueue debugRing, Sequence debugSeq, RingQueue infoRing, Sequence infoSeq, RingQueue errorRing, Sequence errorSeq ) { this.clock = clock; this.name = name; this.debugRing = debugRing; this.debugSeq = debugSeq; this.infoRing = infoRing; this.infoSeq = infoSeq; this.errorRing = errorRing; this.errorSeq = errorSeq; } @Override public void $() { $(Misc.EOL); Holder h = tl.get(); h.seq.done(h.cursor); } @Override public LogRecord $(CharSequence sequence) { if (sequence == null) { sink().put("null"); } else { sink().put(sequence); } return this; } @Override public LogRecord utf8(CharSequence sequence) { if (sequence == null) { sink().put("null"); } else { sink().encodeUtf8(sequence); } return this; } @Override public LogRecord $(int x) { sink().put(x); return this; } @Override public LogRecord $(double x) { sink().put(x, 2); return this; } @Override public LogRecord $(long x) { sink().put(x); return this; } @Override public LogRecord $(char c) { sink().put(c); return this; } @Override public LogRecord $(boolean x) { sink().put(x); return this; } @Override public LogRecord $(Throwable e) { if (e != null) { sink().put(Misc.EOL).put(e); } return this; } @Override public LogRecord $(File x) { sink().put(x == null ? "null" : x.getAbsolutePath()); return this; } @Override public LogRecord $(Enum e) { sink().put(e != null ? e.name() : "null"); return this; } @Override public LogRecord $(Object x) { sink().put(x == null ? "null" : x.toString()); return this; } @Override public LogRecord $(Sinkable x) { if (x == null) { sink().put("null"); } else { x.toSink(sink()); } return this; } @Override public LogRecord $ip(long ip) { Net.appendIP4(sink(), ip); return this; } @Override public LogRecord $ts(long x) { sink().putISODate(x); return this; } @Override public boolean isEnabled() { return true; } @Override public LogRecord ts() { sink().putISODate(clock.getTicks()); return this; } @Override public LogRecord debug() { return xdebug().ts().$(" D ").$(name); } @Override public LogRecord error() { return xerror().ts().$(" E ").$(name); } @Override public LogRecord info() { return xinfo().ts().$(" I ").$(name); } @Override public boolean isDebugEnabled() { return debugSeq != null; } public LogRecord xerror() { return next(errorSeq, errorRing, LogLevel.LOG_LEVEL_ERROR); } public LogRecord xinfo() { return next(infoSeq, infoRing, LogLevel.LOG_LEVEL_INFO); } private LogRecord next(Sequence seq, RingQueue ring, int level) { if (seq == null) { return NullLogRecord.INSTANCE; } long cursor = seq.next(); if (cursor < 0) { return NullLogRecord.INSTANCE; } Holder h = tl.get(); h.cursor = cursor; h.seq = seq; h.ring = ring; LogRecordSink r = ring.get(cursor); r.setLevel(level); r.clear(0); return this; } private CharSink sink() { Holder h = tl.get(); return h.ring.get(h.cursor); } private LogRecord xdebug() { return next(debugSeq, debugRing, LogLevel.LOG_LEVEL_DEBUG); } private static class Holder { private long cursor; private Sequence seq; private RingQueue ring; } private static class ThreadLocalCursor extends ThreadLocal { @Override protected Holder initialValue() { return new Holder(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy