package org.araqne.log.api;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.araqne.log.api.impl.FileUtils;
public class ConfigWatchLogger extends AbstractLogger implements Reconfigurable {
private final org.slf4j.Logger slog = org.slf4j.LoggerFactory.getLogger(ConfigWatchLogger.class);
protected String basePath;
protected Pattern fileNamePattern;
protected String fileTag;
public ConfigWatchLogger(LoggerSpecification spec, LoggerFactory factory) {
super(spec, factory);
basePath = getConfigs().get("base_path");
String fileNameRegex = getConfigs().get("filename_pattern");
fileNamePattern = Pattern.compile(fileNameRegex);
// optional
this.fileTag = getConfigs().get("file_tag");
}
@Override
protected void runOnce() {
List<File> files = FileUtils.matches(basePath, fileNamePattern);
Map<String, ConfigState> states = ConfigState.deserializeMap(getStates());
for (File f : files)
processFile(states, f);
setStates(ConfigState.serializeMap(states));
}
@Override
public void onConfigChange(Map<String, String> oldConfigs, Map<String, String> newConfigs) {
this.fileNamePattern = Pattern.compile(newConfigs.get("filename_pattern"));
this.basePath = newConfigs.get("base_path");
if (!oldConfigs.get("base_path").equals(newConfigs.get("base_path"))
|| !oldConfigs.get("filename_pattern").equals(newConfigs.get("filename_pattern"))) {
setStates(new HashMap<String, Object>());
}
}
protected void processFile(Map<String, ConfigState> states, File f) {
String path = f.getAbsolutePath();
String fullName = getFullName();
try {
if (!f.isFile()) {
slog.debug("araqne log api: logger [{}] skips directory path [{}]", fullName, path);
return;
}
if (!f.canRead()) {
slog.debug("araqne log api: logger [{}] cannot read file [{}], please check permission", fullName, path);
return;
}
String newHash = hash(f);
if (slog.isDebugEnabled())
slog.debug("araqne log api: logger [{}] built hash [{}] for file [{}]", new Object[] { fullName, newHash, path });
ConfigState oldState = states.get(path);
if (oldState != null && !oldState.getHash().equals(newHash)) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("path", path);
params.put("old_hash", oldState.getHash());
params.put("new_hash", newHash);
if (fileTag != null)
params.put(fileTag, f.getName());
Log log = new SimpleLog(new Date(), fullName, params);
write(log);
}
states.put(path, new ConfigState(newHash));
} catch (Throwable t) {
slog.error("araqne log api: logger [" + fullName + "] cannot check file [" + path + "] changes", t);
}
}
private static String hash(File f) throws IOException, NoSuchAlgorithmException {
FileInputStream is = null;
try {
is = new FileInputStream(f);
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] buf = new byte[8096];
while (true) {
int len = is.read(buf, 0, buf.length);
if (len <= 0)
break;
md.update(buf, 0, len);
}
byte[] sha1hash = md.digest();
return convertToHex(sha1hash);
} finally {
if (is != null)
is.close();
}
}
private static String convertToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfbyte = (data[i] >>> 4) & 0x0F;
int two_halfs = 0;
do {
if ((0 <= halfbyte) && (halfbyte <= 9))
buf.append((char) ('0' + halfbyte));
else
buf.append((char) ('a' + (halfbyte - 10)));
halfbyte = data[i] & 0x0F;
} while (two_halfs++ < 1);
}
return buf.toString();
}
}