/*
* Copyright 2013 Eediom Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.araqne.logdb.jython;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicLong;
import org.araqne.api.DateFormat;
import org.araqne.codec.EncodingRule;
import org.araqne.log.api.LastState;
import org.araqne.log.api.Log;
import org.araqne.log.api.LogPipe;
import org.araqne.log.api.LogTransformer;
import org.araqne.log.api.Logger;
import org.araqne.log.api.LoggerEventListener;
import org.araqne.log.api.LoggerFactory;
import org.araqne.log.api.LoggerSpecification;
import org.araqne.log.api.LoggerStartReason;
import org.araqne.log.api.LoggerStatus;
import org.araqne.log.api.LoggerStopReason;
import org.araqne.log.api.SimpleLog;
import org.araqne.log.api.TimeRange;
public abstract class JythonActiveLogger implements Logger, Runnable {
private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JythonActiveLogger.class.getName());
private LoggerFactory factory;
private LoggerSpecification spec;
private LogTransformer transformer;
private CopyOnWriteArraySet<LogPipe> pipes = new CopyOnWriteArraySet<LogPipe>();
private CopyOnWriteArraySet<LoggerEventListener> listeners = new CopyOnWriteArraySet<LoggerEventListener>();
private Thread t;
private int interval;
private Map<String, String> config;
private volatile LoggerStatus status = LoggerStatus.Stopped;
private volatile boolean doStop = false;
private volatile boolean stopped = true;
private volatile boolean enabled;
private TimeRange timeRange;
private volatile boolean pending;
private volatile boolean manualStart;
private volatile Date lastStartDate;
private volatile Date lastRunDate;
private volatile Date lastLogDate;
private AtomicLong logCounter = new AtomicLong();
private AtomicLong dropCounter = new AtomicLong();
private AtomicLong logVolume = new AtomicLong();
private AtomicLong dropVolume = new AtomicLong();
public abstract void init(LoggerSpecification spec);
protected abstract void runOnce();
protected void onStop() {
}
public void preInit(LoggerFactory factory, LoggerSpecification spec) {
this.factory = factory;
this.spec = spec;
this.config = spec.getConfig();
LastState state = getFactory().getLastStateService().getState(getFullName());
if (state != null) {
enabled = state.isEnabled();
if (state.getStartTime() != null && state.getEndTime() != null) {
timeRange = new TimeRange(state.getStartTime(), state.getEndTime());
}
}
}
@Override
public void run() {
stopped = false;
try {
while (true) {
try {
if (doStop)
break;
long startedAt = System.currentTimeMillis();
runOnce();
updateConfigs(config);
long elapsed = System.currentTimeMillis() - startedAt;
lastRunDate = new Date();
if (interval - elapsed < 0)
continue;
Thread.sleep(interval - elapsed);
} catch (InterruptedException e) {
}
}
} catch (Exception e) {
log.error("araqne log api: logger stopped", e);
} finally {
status = LoggerStatus.Stopped;
stopped = true;
doStop = false;
try {
onStop();
} catch (Exception e) {
log.warn("araqne log api: [" + getFullName() + "] stop callback should not throw any exception", e);
}
}
}
@Override
public String getFullName() {
return spec.getNamespace() + "\\" + spec.getName();
}
@Override
public String getNamespace() {
return spec.getNamespace();
}
@Override
public String getName() {
return spec.getName();
}
@Override
public String getFactoryFullName() {
return factory.getFullName();
}
@Override
public String getFactoryName() {
return factory.getName();
}
@Override
public String getFactoryNamespace() {
return factory.getNamespace();
}
@Override
public String getDescription() {
return spec.getDescription();
}
@Override
public boolean isPassive() {
return false;
}
@Override
public Date getLastStartDate() {
return lastStartDate;
}
@Override
public Date getLastRunDate() {
return lastRunDate;
}
@Override
public Date getLastLogDate() {
return lastLogDate;
}
@Override
public long getLogCount() {
return logCounter.get();
}
@Override
public TimeRange getTimeRange() {
return timeRange;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isRunning() {
return !stopped;
}
@Override
public LoggerStatus getStatus() {
return status;
}
@Override
public int getInterval() {
return interval;
}
@Override
public long getDropVolume() {
return dropVolume.get();
}
@Override
public long getLogVolume() {
return logVolume.get();
}
@Override
public void start(LoggerStartReason reason) {
throw new UnsupportedOperationException("this is active logger, start with interval");
}
@Override
public void start(LoggerStartReason reason, int interval) {
if (!stopped)
throw new IllegalStateException("logger is already running");
if (reason == LoggerStartReason.USER_REQUEST)
enabled = true;
status = LoggerStatus.Starting;
this.interval = interval;
invokeStartCallback();
t = new Thread(this, "Logger [" + getFullName() + "]");
t.start();
}
@Override
public void stop(LoggerStopReason reason) {
stop(reason, 0);
}
@Override
public void stop(LoggerStopReason reason, int maxWaitTime) {
status = LoggerStatus.Stopping;
doStop = true;
if (reason == LoggerStopReason.USER_REQUEST)
enabled = false;
invokeStopCallback(reason);
if (t != null) {
if (!t.isAlive()) {
t = null;
return;
}
t.interrupt();
t = null;
}
long begin = new Date().getTime();
try {
while (true) {
if (stopped)
break;
if (maxWaitTime != 0 && new Date().getTime() - begin > maxWaitTime)
break;
Thread.sleep(50);
}
} catch (InterruptedException e) {
}
}
@Override
public void addLogPipe(LogPipe pipe) {
pipes.add(pipe);
}
@Override
public void removeLogPipe(LogPipe pipe) {
pipes.remove(pipe);
}
@Override
public void addEventListener(LoggerEventListener callback) {
listeners.add(callback);
}
@Override
public void removeEventListener(LoggerEventListener callback) {
listeners.remove(callback);
}
@Override
public void clearEventListeners() {
listeners.clear();
}
protected void write(Log log) {
if (stopped)
return;
long volume = getDataLength(log);
// update last log date
lastLogDate = log.getDate();
logCounter.incrementAndGet();
logVolume.addAndGet(volume);
// transform
if (transformer != null) {
log = transformer.transform(log);
}
if (log == null) {
dropCounter.incrementAndGet();
dropVolume.addAndGet(volume);
return;
}
// notify all
for (LogPipe pipe : pipes) {
try {
pipe.onLog(this, log);
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().startsWith("invalid time"))
this.log.warn("araqne logdb jython: log pipe should not throw exception" + e.getMessage());
else
this.log.warn("araqne logdb jython: log pipe should not throw exception", e);
}
}
}
private long getDataLength(Log log) {
if (log instanceof SimpleLog) {
SimpleLog sl = (SimpleLog) log;
if (sl.getDataLength() == 0)
return EncodingRule.lengthOfMap(log.getParams());
else
return sl.getDataLength();
} else {
return EncodingRule.lengthOfMap(log.getParams());
}
}
@Override
public void updateConfigs(Map<String, String> config) {
for (LoggerEventListener callback : listeners) {
try {
callback.onUpdated(this, config);
} catch (Exception e) {
log.error("araqne log api: logger event callback should not throw any exception", e);
}
}
}
@Override
public Map<String, String> getConfig() {
return config;
}
private void invokeStartCallback() {
lastStartDate = new Date();
status = LoggerStatus.Running;
LastState s = new LastState();
s.setLoggerName(getFullName());
s.setInterval(interval);
if (timeRange != null) {
s.setStartTime(timeRange.getStartTime());
s.setEndTime(timeRange.getEndTime());
}
s.setLogCount(getLogCount());
s.setDropCount(getDropCount());
s.setLastLogDate(getLastLogDate());
s.setPending(isPending());
s.setProperties(getStates());
s.setEnabled(isEnabled());
s.setRunning(isRunning());
s.setLogVolume(getLogVolume());
s.setDropVolume(getDropVolume());
getFactory().getLastStateService().setState(s);
for (LoggerEventListener callback : listeners) {
try {
callback.onStart(this);
} catch (Exception e) {
log.warn("logger callback should not throw any exception", e);
}
}
}
private void invokeStopCallback(LoggerStopReason reason) {
LastState s = new LastState();
s.setLoggerName(getFullName());
s.setInterval(interval);
if (timeRange != null) {
s.setStartTime(timeRange.getStartTime());
s.setEndTime(timeRange.getEndTime());
}
s.setLogCount(getLogCount());
s.setDropCount(getDropCount());
s.setLastLogDate(getLastLogDate());
s.setPending(isPending());
s.setProperties(getStates());
s.setEnabled(false);
s.setRunning(isRunning());
s.setLogVolume(getLogVolume());
s.setDropVolume(getDropVolume());
getFactory().getLastStateService().setState(s);
for (LoggerEventListener callback : listeners) {
try {
callback.onStop(this, reason);
} catch (Exception e) {
log.warn("logger callback should not throw any exception", e);
}
}
}
@Override
public long getDropCount() {
return dropCounter.get();
}
@Override
public boolean isPending() {
return pending;
}
@Override
public void setPending(boolean pending) {
this.pending = pending;
}
@Override
public boolean isManualStart() {
return manualStart;
}
@Override
public void setManualStart(boolean manualStart) {
this.manualStart = manualStart;
}
@Override
public LogTransformer getTransformer() {
return transformer;
}
@Override
public void setTransformer(LogTransformer transformer) {
this.transformer = transformer;
}
@Override
public String toString() {
String format = "yyyy-MM-dd HH:mm:ss";
String start = DateFormat.format(format, lastStartDate);
String run = DateFormat.format(format, lastRunDate);
String log = DateFormat.format(format, lastLogDate);
String status = getStatus().toString().toLowerCase();
status += " (interval=" + interval + "ms)";
return String.format("name=%s, factory=%s, script=%s, status=%s, log count=%d, last start=%s, last run=%s, last log=%s",
getFullName(), factory.getFullName(), getClass().getSimpleName(), status, getLogCount(), start, run, log);
}
}