/*
* Copyright 2010 NCHOVY
*
* 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.logstorage.script;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.araqne.api.PathAutoCompleter;
import org.araqne.api.Script;
import org.araqne.api.ScriptArgument;
import org.araqne.api.ScriptContext;
import org.araqne.api.ScriptUsage;
import org.araqne.api.SystemProperty;
import org.araqne.confdb.ConfigService;
import org.araqne.log.api.FieldDefinition;
import org.araqne.log.api.WildcardMatcher;
import org.araqne.logstorage.*;
import org.araqne.logstorage.LogTraverseCallback.BlockSkipReason;
import org.araqne.logstorage.engine.ConfigUtil;
import org.araqne.logstorage.engine.Constants;
import org.araqne.storage.api.FilePath;
import org.araqne.storage.api.StorageManager;
import org.araqne.storage.api.URIResolver;
import org.araqne.storage.localfile.LocalFilePath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogStorageScript implements Script {
private final Logger logger = LoggerFactory.getLogger(LogStorageScript.class);
private ScriptContext context;
private LogTableRegistry tableRegistry;
private LogStorage storage;
private LogStorageMonitor monitor;
private ConfigService conf;
private LogFileServiceRegistry lfsRegistry;
private LogCryptoProfileRegistry cryptoRegistry;
private StorageManager storageManager;
public LogStorageScript(LogTableRegistry tableRegistry, LogStorage archive, LogStorageMonitor monitor, ConfigService conf,
LogFileServiceRegistry lfsRegistry, LogCryptoProfileRegistry cryptoRegistry, StorageManager storageManager) {
this.tableRegistry = tableRegistry;
this.storage = archive;
this.monitor = monitor;
this.conf = conf;
this.lfsRegistry = lfsRegistry;
this.cryptoRegistry = cryptoRegistry;
this.storageManager = storageManager;
}
@Override
public void setScriptContext(ScriptContext context) {
this.context = context;
}
public void cryptoProfiles(String[] args) {
context.println("Crypto Profiles");
context.println("-----------------");
for (LogCryptoProfile p : cryptoRegistry.getProfiles()) {
context.println(p);
}
}
@ScriptUsage(description = "create crypto profile", arguments = { @ScriptArgument(name = "profile name", type = "string", description = "crypto profile name") })
public void createCryptoProfile(String[] args) throws InterruptedException {
LogCryptoProfile p = new LogCryptoProfile();
p.setName(args[0]);
context.print("pkcs12 path? ");
String line = context.readLine().trim();
p.setFilePath(line);
context.print("pkcs12 password? ");
line = context.readLine();
p.setPassword(line);
context.print("cipher algorithm? ");
line = context.readLine().trim();
p.setCipher(line.trim().isEmpty() ? null : line);
context.print("digest algorithm? ");
line = context.readLine().trim();
p.setDigest(line.trim().isEmpty() ? null : line);
cryptoRegistry.addProfile(p);
context.println("created");
}
@ScriptUsage(description = "create crypto profile", arguments = { @ScriptArgument(name = "profile name", type = "string", description = "crypto profile name") })
public void removeCryptoProfile(String[] args) {
cryptoRegistry.removeProfile(args[0]);
context.println("removed");
}
public void forceRetentionCheck(String[] args) {
monitor.forceRetentionCheck();
context.println("triggered");
}
@ScriptUsage(description = "set retention policy", arguments = { @ScriptArgument(name = "table name", type = "string", description = "table name") })
public void retention(String[] args) {
String tableName = args[0];
LogRetentionPolicy p = storage.getRetentionPolicy(tableName);
context.println(p.getRetentionDays() + "days");
}
@ScriptUsage(description = "set retention policy", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "retention days", type = "int", description = "retention days (0 for infinite)") })
public void setRetention(String[] args) {
LogRetentionPolicy p = new LogRetentionPolicy();
p.setTableName(args[0]);
p.setRetentionDays(Integer.valueOf(args[1]));
storage.setRetentionPolicy(p);
context.println("set");
}
@ScriptUsage(description = "print table metadata", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "table metadata key", type = "string", description = "key", optional = true),
@ScriptArgument(name = "table metadata value", type = "string", description = "value", optional = true) })
public void table(String[] args) {
String lang = System.getProperty("user.language");
Locale locale = Locale.ENGLISH;
if (lang != null)
locale = new Locale(lang);
String tableName = args[0];
if (!tableRegistry.exists(tableName)) {
context.println("table not found");
return;
}
if (args.length == 1) {
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
List<FieldDefinition> fields = schema.getFieldDefinitions();
StorageConfig primaryStorage = schema.getPrimaryStorage();
LogFileService lfs = lfsRegistry.getLogFileService(primaryStorage.getType());
context.println("Storage Configs for " + primaryStorage.getType());
context.println("-------------------------");
// primary storage
for (TableConfigSpec spec : lfs.getConfigSpecs()) {
TableConfig c = primaryStorage.getConfig(spec.getKey());
String config = null;
if (c != null && c.getValues().size() > 1)
config = c.getValues().toString();
else if (c != null)
config = c.getValue();
context.println(spec.getDisplayNames().get(locale) + ": " + config);
}
// replica storage
StorageConfig replicaStorage = schema.getReplicaStorage();
if (replicaStorage != null) {
for (TableConfigSpec spec : lfs.getReplicaConfigSpecs()) {
TableConfig c = replicaStorage.getConfig(spec.getKey());
String config = null;
if (c != null && c.getValues().size() > 1)
config = c.getValues().toString();
else if (c != null)
config = c.getValue();
context.println(spec.getDisplayNames().get(locale) + ": " + config);
}
}
// TODO : handle secondary storages
context.println("");
if (fields != null) {
context.println("Field Definitions");
context.println("-------------------");
for (FieldDefinition field : fields) {
String line = null;
if (field.getLength() > 0)
line = field.getName() + "\t" + field.getType() + "(" + field.getLength() + ")";
line = field.getName() + "\t" + field.getType();
context.println(line);
}
context.println("");
}
Map<String, String> metadata = schema.getMetadata();
if (metadata != null && metadata.size() > 0) {
context.println("Table Metadata");
context.println("----------------");
for (String key : metadata.keySet()) {
String value = metadata.get(key);
context.println(key + "=" + value);
}
context.println();
}
long total = storage.getDiskUsage(tableName, null, null);
LogRetentionPolicy retentionPolicy = storage.getRetentionPolicy(tableName);
String retention = "None";
if (retentionPolicy != null && retentionPolicy.getRetentionDays() > 0)
retention = retentionPolicy.getRetentionDays() + "days";
context.println("Storage Information");
context.println("---------------------");
context.println("Retention Policy: " + retention);
context.println("Data path: " + storage.getTableDirectory(tableName).getAbsolutePath());
NumberFormat nf = NumberFormat.getNumberInstance();
context.println("Consumption: " + nf.format(total) + " bytes");
LockStatus status = storage.lockStatus(new LockKey("script", args[0], null));
if (status.isLocked())
context.printf("Lock status: locked (owner: %s, purpose: %s, reentrant_cnt: %d)\n", status.getOwner(),
status.getPurposes(), status.getReentrantCount());
else
context.printf("Lock status: unlocked\n");
context.println("");
} else if (args.length == 2) {
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
String value = schema.getMetadata().remove(args[1]);
tableRegistry.alterTable(tableName, schema);
context.println("unset " + value);
} else if (args.length == 3) {
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
schema.getMetadata().put(args[1], args[2]);
tableRegistry.alterTable(tableName, schema);
context.printf("set %s to %s\n", args[1], args[2]);
}
}
@ScriptUsage(description = "list tables", arguments = { @ScriptArgument(name = "filter", type = "string", description = "table name filter", optional = true) })
public void tables(String[] args) {
String filter = null;
OptionParser parser = new OptionParser();
parser.accepts("lock");
parser.accepts("replica");
OptionSet options = parser.parse(args);
List<?> argl = options.nonOptionArguments();
if (argl.size() > 0)
filter = (String) argl.get(0);
context.println("Tables");
context.println("--------");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
ArrayList<TableInfo> tables = new ArrayList<TableInfo>();
for (TableSchema schema : tableRegistry.getTableSchemas()) {
String tableName = schema.getName();
if (filter != null && !tableName.contains(filter))
continue;
int tableId = schema.getId();
Iterator<Date> it = storage.getLogDates(tableName).iterator();
Date lastDay = null;
if (it.hasNext())
lastDay = it.next();
String desc;
if (options.has("lock"))
desc = lockStatusStr(tableName);
else if (options.has("replica"))
desc = replicaConfigStr(schema);
else
desc = lastDay != null ? dateFormat.format(DateUtil.getDay(lastDay)) : "none";
tables.add(new TableInfo(tableId, "[" + tableId + "] " + tableName + ": " + desc));
}
// sort by id and print all
Collections.sort(tables, new Comparator<TableInfo>() {
@Override
public int compare(TableInfo o1, TableInfo o2) {
return o1.id - o2.id;
}
});
for (TableInfo t : tables)
context.println(t.info);
}
private String replicaConfigStr(TableSchema schema) {
StorageConfig primaryStorage = schema.getPrimaryStorage();
LogFileService lfs = lfsRegistry.getLogFileService(primaryStorage.getType());
StorageConfig replicaStorage = schema.getReplicaStorage();
if (replicaStorage != null) {
StringBuffer buf = new StringBuffer();
for (TableConfigSpec spec : lfs.getReplicaConfigSpecs()) {
TableConfig c = replicaStorage.getConfig(spec.getKey());
String config = null;
if (c != null && c.getValues().size() > 1)
config = c.getValues().toString();
else if (c != null)
config = c.getValue();
if (buf.length() != 0)
buf.append(", ");
buf.append(config);
}
return buf.toString();
} else {
return "n/a";
}
}
private String lockStatusStr(String tableName) {
LockStatus s = storage.lockStatus(new LockKey("script", tableName, null));
if (s.isLocked())
return String.format("locked(owner: %s, reentrant_cnt: %d, purpose(s): %s)", s.getOwner(), s.getReentrantCount(),
Arrays.toString(s.getPurposes().toArray()));
else
return "unlocked";
}
private class TableInfo {
public int id;
public String info;
public TableInfo(int id, String info) {
this.id = id;
this.info = info;
}
}
public void open(String[] args) {
storage.start();
context.println("opened");
}
public void close(String[] args) {
storage.stop();
context.println("closed");
}
public void reload(String[] args) {
storage.reload();
}
@ScriptUsage(description = "create new table", arguments = {
@ScriptArgument(name = "name", type = "string", description = "log table name"),
@ScriptArgument(name = "engine type", type = "string", description = "engine type (v1, v2, etc)") })
public void createTable(String[] args) {
if (args.length > 2)
context.println("WARN: storage engine config and metadata will be set separately.");
String lang = System.getProperty("user.language");
Locale locale = Locale.ENGLISH;
if (lang != null)
locale = new Locale(lang);
try {
String basePath = readLine("Base Path? (optional, enter to skip)? ");
StorageConfig primaryStorage = new StorageConfig(args[1], basePath);
TableSchema schema = new TableSchema(args[0], primaryStorage);
String engineType = args[1];
LogFileService lfs = lfsRegistry.getLogFileService(engineType);
// primary storage
for (TableConfigSpec spec : lfs.getConfigSpecs()) {
while (true) {
String optional = spec.isOptional() ? " (optional, enter to skip)" : "";
String line = readLine(spec.getDisplayNames().get(locale) + optional + "? ");
if (line != null) {
try {
if (spec.getValidator() != null)
spec.getValidator().validate(spec.getKey(), Arrays.asList(line));
} catch (Throwable t) {
context.println(t.getMessage());
continue;
}
primaryStorage.getConfigs().add(new TableConfig(spec.getKey(), line));
break;
} else if (spec.isOptional())
break;
}
}
// replica storage
// TODO check replica storage initiation
StorageConfig replicaStorage = primaryStorage.clone();
schema.setReplicaStorage(replicaStorage);
for (TableConfigSpec spec : lfs.getReplicaConfigSpecs()) {
while (true) {
String optional = spec.isOptional() ? " (optional, enter to skip)" : "";
String line = readLine(spec.getDisplayNames().get(locale) + optional + "? ");
if (line != null) {
try {
if (spec.getValidator() != null)
spec.getValidator().validate(spec.getKey(), Arrays.asList(line));
} catch (Throwable t) {
context.println(t.getMessage());
continue;
}
replicaStorage.getConfigs().add(new TableConfig(spec.getKey(), line));
break;
} else if (spec.isOptional())
break;
}
}
// TODO secondary storages
Map<String, String> metadata = new HashMap<String, String>();
if (args.length > 2) {
for (int i = 2; i < args.length; i++) {
String[] pair = args[i].split("=");
if (pair.length != 2)
continue;
metadata.put(pair[0], pair[1]);
}
}
schema.setMetadata(metadata);
storage.createTable(schema);
context.println("table created");
} catch (InterruptedException e) {
context.println("");
context.println("interrupted");
} catch (Throwable t) {
context.println("cannot create table " + args[0] + ": " + t.getMessage());
logger.error("araqne logstorage: cannot create table " + args[0], t);
}
}
@ScriptUsage(description = "alter table", arguments = { @ScriptArgument(name = "name", type = "string", description = "log table name") })
public void alterTable(String[] args) {
String lang = System.getProperty("user.language");
Locale locale = Locale.ENGLISH;
if (lang != null)
locale = new Locale(lang);
int count = 0;
String tableName = args[0];
try {
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
LogFileService lfs = lfsRegistry.getLogFileService(schema.getPrimaryStorage().getType());
// primary storage
StorageConfig primaryStorage = schema.getPrimaryStorage();
for (TableConfigSpec spec : lfs.getConfigSpecs()) {
if (!spec.isUpdatable())
continue;
count++;
TableConfig config = primaryStorage.getConfig(spec.getKey());
while (true) {
String optional = spec.isOptional() ? " (optional, enter to drop)" : "";
String line = readLine(spec.getDisplayNames().get(locale) + optional + "? ");
if (line != null) {
primaryStorage.getConfigs().remove(config);
primaryStorage.getConfigs().add(new TableConfig(spec.getKey(), line));
break;
} else if (spec.isOptional()) {
primaryStorage.getConfigs().remove(config);
break;
}
}
}
// replica storage
StorageConfig replicaStorage = schema.getReplicaStorage();
if (replicaStorage == null) {
replicaStorage = primaryStorage.clone();
schema.setReplicaStorage(replicaStorage);
}
for (TableConfigSpec spec : lfs.getReplicaConfigSpecs()) {
if (!spec.isUpdatable())
continue;
count++;
TableConfig config = replicaStorage.getConfig(spec.getKey());
while (true) {
String optional = spec.isOptional() ? " (optional, enter to drop)" : "";
String line = readLine(spec.getDisplayNames().get(locale) + optional + "? ");
if (line != null) {
replicaStorage.getConfigs().remove(config);
replicaStorage.getConfigs().add(new TableConfig(spec.getKey(), line));
break;
} else if (spec.isOptional()) {
replicaStorage.getConfigs().remove(config);
break;
}
}
}
// TODO secondary storage
storage.alterTable(args[0], schema);
} catch (InterruptedException e) {
context.println("");
context.println("interrupted");
} catch (Throwable t) {
context.println("cannot alter table " + args[0] + ": " + t.getMessage());
logger.error("araqne logstorage: cannot alter table " + args[0], t);
}
if (count == 0)
context.println("no updatable configs");
}
private String readLine(String question) throws InterruptedException {
context.print(question);
String line = context.readLine();
if (line.trim().isEmpty())
return null;
return line;
}
@ScriptUsage(description = "drop log table", arguments = { @ScriptArgument(name = "name", type = "string", description = "log table name") })
public void dropTable(String[] args) {
try {
Collection<Date> dates = storage.getLogDates(args[0]);
if (SystemProperty.isEnabled("cc_compliant") && !dates.isEmpty()) {
context.println("cannot drop the table");
return;
}
storage.dropTable(args[0]);
context.println("table dropped");
} catch (Exception e) {
context.println(e.getMessage());
}
}
@ScriptUsage(description = "set table fields", arguments = { @ScriptArgument(name = "table name", type = "string", description = "table name") })
public void setFields(String[] args) {
String tableName = args[0];
try {
List<FieldDefinition> fields = new ArrayList<FieldDefinition>();
context.println("Use 'name type(length)' format. Length can be omitted.");
context.println("Allowed types: string, short, int, long, float, double, date, and bool");
context.println("Enter empty line to finish.");
while (true) {
context.print("field definition? ");
String line = context.readLine();
if (line.isEmpty())
break;
FieldDefinition field = FieldDefinition.parse(line);
fields.add(field);
}
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
schema.setFieldDefinitions(fields);
tableRegistry.alterTable(tableName, schema);
context.println("schema changed");
} catch (Throwable t) {
context.println("cannot update schema: " + t.getMessage());
logger.error("araqne logstorage: cannot update table fields", t);
}
}
@ScriptUsage(description = "unset table fields", arguments = { @ScriptArgument(name = "table name", type = "string", description = "table name") })
public void unsetFields(String[] args) {
String tableName = args[0];
TableSchema schema = tableRegistry.getTableSchema(tableName, true);
schema.setFieldDefinitions(null);
tableRegistry.alterTable(tableName, schema);
context.println("schema changed");
}
@ScriptUsage(description = "get logs", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "from", type = "string", description = "yyyyMMddHH format"),
@ScriptArgument(name = "to", type = "string", description = "yyyyMMddHH format"),
@ScriptArgument(name = "offset", type = "int", description = "offset"),
@ScriptArgument(name = "limit", type = "int", description = "log limit") })
public void logs(String[] args) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH");
String tableName = args[0];
Date from = dateFormat.parse(args[1]);
Date to = dateFormat.parse(args[2]);
int offset = Integer.valueOf(args[3]);
int limit = Integer.valueOf(args[4]);
try {
LogTraverseCallback.Sink contextSink = new LogTraverseCallback.Sink(offset, limit) {
@Override
protected void processLogs(List<Log> logs) {
for (Log log : logs)
context.println(log.toString());
}
};
storage.search(new TableScanRequest(tableName, from, to, null, new SimpleLogTraverseCallback(contextSink)));
} catch (InterruptedException e) {
context.println("interrupted");
}
}
@ScriptUsage(description = "search table", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "log table name"),
@ScriptArgument(name = "from", type = "string", description = "from"),
@ScriptArgument(name = "to", type = "string", description = "to"),
@ScriptArgument(name = "limit", type = "int", description = "count limit") })
public void searchTable(String[] args) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String tableName = args[0];
Date from = dateFormat.parse(args[1]);
Date to = dateFormat.parse(args[2]);
int limit = Integer.parseInt(args[3]);
long begin = new Date().getTime();
LogTraverseCallback.Sink printSink = new LogTraverseCallback.Sink(0, limit) {
@Override
protected void processLogs(List<Log> logs) {
for (Log log : logs) {
Map<String, Object> m = log.getData();
context.print(log.getId() + ": ");
for (String key : m.keySet()) {
context.print(key + "=" + m.get(key) + ", ");
}
context.println("");
}
}
};
TableScanRequest req = new TableScanRequest(tableName, from, to, null, new SimpleLogTraverseCallback(printSink));
storage.search(req);
long end = new Date().getTime();
context.println("elapsed: " + (end - begin) + "ms");
} catch (Exception e) {
context.println(e.getMessage());
}
}
@ScriptUsage(description = "print all parameters")
public void parameters(String[] args) {
for (Constants c : Constants.values()) {
context.println(c.getName() + ": " + ConfigUtil.get(conf, c));
}
}
@ScriptUsage(description = "set parameters", arguments = { @ScriptArgument(name = "key", type = "string", description = "parameter key") })
public void unsetParameter(String[] args) {
Constants configKey = Constants.parse(args[0]);
if (configKey == null) {
context.println("invalid key name");
return;
}
ConfigUtil.set(conf, configKey, null);
context.println("unset");
}
@ScriptUsage(description = "set parameters", arguments = {
@ScriptArgument(name = "key", type = "string", description = "parameter key"),
@ScriptArgument(name = "value", type = "string", description = "parameter value") })
public void setParameter(String[] args) {
Constants configKey = Constants.parse(args[0]);
if (configKey == null) {
context.println("invalid key name");
return;
}
String value = null;
if (configKey.getType().equals("string")) {
value = args[1];
} else if (configKey.getType().equals("int")) {
int interval = 0;
try {
interval = Integer.parseInt(args[1]);
value = Integer.toString(interval);
} catch (NumberFormatException e) {
context.println("invalid parameter format");
return;
}
}
ConfigUtil.set(conf, configKey, value);
context.println("set");
}
public static class LocalStorageManager implements StorageManager {
@Override
public FilePath resolveFilePath(String path) {
return new LocalFilePath(path);
}
@Override
public void start() {
}
@Override
public void stop() {
}
@Override
public void addURIResolver(URIResolver r) {
throw new UnsupportedOperationException();
}
}
private static List<FilePath> getMatchingFiles(StorageManager storageManager, String s, FilePath workingDir) {
FilePath root = getListRoot(storageManager, s);
Stack<FilePath> dirs = new Stack<FilePath>();
dirs.push(root);
if (workingDir == null) {
s = storageManager.resolveFilePath(s).getAbsolutePath().replaceAll("\\\\", "/");
} else {
s = workingDir.newFilePath(s).getAbsolutePath().replaceAll("\\\\", "/");
}
Pattern p = WildcardMatcher.buildPattern(s);
List<FilePath> result = new ArrayList<FilePath>();
while (!dirs.isEmpty()) {
FilePath cur = dirs.pop();
FilePath[] files = null;
if ((files = cur.listFiles()) == null)
continue;
for (FilePath f : files) {
if (f.isDirectory()) {
dirs.push(f);
continue;
}
if (p.matcher(f.getAbsolutePath().replaceAll("\\\\", "/")).matches())
result.add(f);
}
}
Collections.sort(result);
return result;
}
private static FilePath getListRoot(StorageManager storageManager, String s) {
FilePath parent = storageManager.resolveFilePath(s);
while (true) {
if (parent.getAbsolutePath().contains("*"))
parent = parent.getAbsoluteFilePath();
else
break;
}
return parent;
}
@ScriptUsage(description = "import text log file", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "file path", type = "string", description = "text log file path", autocompletion = PathAutoCompleter.class),
@ScriptArgument(name = "offset", type = "int", description = "skip offset", optional = true),
@ScriptArgument(name = "limit", type = "int", description = "load limit count", optional = true) })
public void importTextFile(String[] args) throws IOException {
String tableName = args[0];
if (args[1].contains("*")) {
int offset = 0;
int limit = Integer.MAX_VALUE;
List<FilePath> files = getMatchingFiles(storageManager, args[1], null);
int cur = 1;
for (FilePath f : files) {
String startedAt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
context.printf("[%s] loading (%d/%d) %s\n", startedAt, cur, files.size(), f.getAbsolutePath());
importFile(tableName, f, offset, limit);
cur += 1;
}
} else {
FilePath file = storageManager.resolveFilePath(args[1]);
int offset = 0;
if (args.length > 2)
offset = Integer.valueOf(args[2]);
int limit = Integer.MAX_VALUE;
if (args.length > 3)
limit = Integer.valueOf(args[3]);
importFile(tableName, file, offset, limit);
}
}
private void importFile(String tableName, FilePath file, int offset, int limit) throws IOException {
InputStream is = null;
try {
is = file.newInputStream();
if (file.getName().endsWith(".gz")) {
is = new GZIPInputStream(is);
}
importFromStream(tableName, is, offset, limit);
} catch (Exception e) {
context.println("import failed, " + e.getMessage());
logger.error("araqne logstorage: cannot import text file " + file.getAbsolutePath(), e);
} finally {
if (is != null)
is.close();
}
}
@ScriptUsage(description = "import zipped text log file", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "zip file path", type = "string", description = "zip file path", autocompletion = PathAutoCompleter.class),
@ScriptArgument(name = "entry path", type = "string", description = "zip entry of text log file path"),
@ScriptArgument(name = "offset", type = "int", description = "skip offset", optional = true),
@ScriptArgument(name = "limit", type = "int", description = "load limit count", optional = true) })
public void importZipFile(String[] args) throws ZipException, IOException {
String tableName = args[0];
String filePath = args[1];
String entryPath = args[2];
File file = new File(args[1]);
int offset = 0;
if (args.length > 3)
offset = Integer.valueOf(args[3]);
int limit = Integer.MAX_VALUE;
if (args.length > 4)
limit = Integer.valueOf(args[4]);
ZipFile zipFile = null;
try {
zipFile = new ZipFile(file);
ZipEntry entry = zipFile.getEntry(entryPath);
if (entry == null) {
context.println("entry [" + entryPath + "] not found in zip file [" + filePath + "]");
return;
}
InputStream is = null;
try {
is = zipFile.getInputStream(entry);
importFromStream(tableName, is, offset, limit);
} catch (Exception e) {
context.println("import failed, " + e.getMessage());
logger.error("araqne logstorage: cannot import zipped text file " + file.getAbsolutePath(), e);
} finally {
if (is != null)
is.close();
}
} finally {
zipFile.close();
}
}
private void importFromStream(String tableName, InputStream fis, int offset, int limit) throws IOException,
InterruptedException {
Date begin = new Date();
long count = 0;
BufferedReader br = new BufferedReader(new InputStreamReader(fis), 16384 * 1024); // 16MB
String line = null;
ArrayList<Log> buf = new ArrayList<Log>(1000);
int i = 0;
long lastCheck = System.currentTimeMillis();
long lastCount = 0;
double lps = 0.0;
while (true) {
line = br.readLine();
if (line == null)
break;
if (count >= limit)
break;
if (i++ < offset)
continue;
Map<String, Object> m = new HashMap<String, Object>();
m.put("line", line);
Log log = new Log(tableName, new Date(), m);
buf.add(log);
count++;
if (count % 1000 == 0) {
try {
storage.write(buf);
buf = new ArrayList<Log>(1000);
} catch (IllegalArgumentException e) {
context.println("skip " + line + ", " + e.getMessage());
}
}
if (count % 10000 == 0) {
context.print("\rloaded " + count + (lps != 0.0 ? String.format(" (%.2f lps)", lps) : ""));
long curTime = System.currentTimeMillis();
if (curTime - lastCheck > 1000) {
lps = (count - lastCount) / ((double) (curTime - lastCheck) / 1000);
lastCount = count;
lastCheck = curTime;
}
}
}
if (buf.size() > 0)
storage.write(buf);
long milliseconds = new Date().getTime() - begin.getTime();
long speed = count * 1000 / milliseconds;
context.println("\rloaded " + count + " logs in " + milliseconds + " ms, " + speed + " logs/sec");
}
@ScriptUsage(description = "benchmark table fullscan", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "from", type = "string", description = "date from (yyyyMMdd format)"),
@ScriptArgument(name = "to", type = "string", description = "date to (yyyyMMdd format)") })
public void fullscan(String[] args) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String tableName = args[0];
Date from = dateFormat.parse(args[1]);
Date to = dateFormat.parse(args[2]);
CounterSink counter = new CounterSink(Integer.MAX_VALUE);
Date timestamp = new Date();
storage.search(new TableScanRequest(tableName, from, to, null, new SimpleLogTraverseCallback(counter)));
long elapsed = new Date().getTime() - timestamp.getTime();
context.println("total count: " + counter.getCount() + ", elapsed: " + elapsed + "ms");
} catch (ParseException e) {
context.println("invalid date format");
} catch (InterruptedException e) {
context.println("interrupted");
}
}
private class CounterSink extends LogTraverseCallback.Sink {
/** guarded by this */
private int count;
/** guarded by this */
private long minId;
/** guarded by this */
private long maxId;
@SuppressWarnings("unused")
long rsvCnt = 0;
@SuppressWarnings("unused")
long fixCnt = 0;
public CounterSink(long limit) {
super(0, limit);
this.count = 0;
this.minId = Long.MAX_VALUE;
this.maxId = Long.MIN_VALUE;
}
public synchronized int getCount() {
return count;
}
@Override
protected void onBlockSkipped(BlockSkipReason reason, long firstId, int logCount) {
if (reason.equals(BlockSkipReason.Reserved))
rsvCnt += logCount;
else if (reason.equals(BlockSkipReason.Fixed))
fixCnt += logCount;
}
@Override
protected void processLogs(List<Log> logs) {
long min = Long.MAX_VALUE;
long max = Long.MIN_VALUE;
for (Log l : logs) {
long id = l.getId();
if (min > id)
min = id;
if (max < id)
max = id;
}
synchronized (this) {
count += logs.size();
if (minId > min)
minId = min;
if (maxId < max)
maxId = max;
}
}
}
public void flush(String[] args) {
storage.flush();
}
@ScriptUsage(description = "print all online writer statuses")
public void writers(String[] args) {
context.println("Online Writers");
context.println("-----------------");
for (LogWriterStatus s : storage.getWriterStatuses()) {
context.println(s);
}
}
public void supportedLogFileTypes(String[] args) {
for (String type : lfsRegistry.getServiceTypes()) {
context.println(type);
}
}
@ScriptUsage(description = "print current engine configs", arguments = { @ScriptArgument(name = "engine type", type = "string", description = "v1, v2, or else") })
public void engineConfigs(String[] args) {
LogFileService s = lfsRegistry.getLogFileService(args[0]);
if (s == null) {
context.println("no engine found");
return;
}
context.println(s.getConfigs().toString());
}
@ScriptUsage(description = "set engine config", arguments = {
@ScriptArgument(name = "engine type", type = "string", description = "v1, v2, or else"),
@ScriptArgument(name = "key", type = "string", description = "config key"),
@ScriptArgument(name = "value", type = "string", description = "config value") })
public void setEngine(String[] args) {
LogFileService s = lfsRegistry.getLogFileService(args[0]);
if (s == null) {
context.println("no engine found");
return;
}
s.setConfig(args[1], args[2]);
context.println("set");
}
@ScriptUsage(description = "unset engine config", arguments = {
@ScriptArgument(name = "engine type", type = "string", description = "v1, v2, or else"),
@ScriptArgument(name = "key", type = "string", description = "config key") })
public void unsetEngine(String[] args) {
LogFileService s = lfsRegistry.getLogFileService(args[0]);
if (s == null) {
context.println("no engine found");
return;
}
s.unsetConfig(args[1]);
context.println("unset");
}
@ScriptUsage(description = "", arguments = {
@ScriptArgument(name = "count", type = "integer", description = "log count", optional = true),
@ScriptArgument(name = "repeat", type = "integer", description = "repeat count", optional = true) })
public void benchmark(String[] args) throws InterruptedException {
String tableName = "benchmark";
int count = 1000000;
int repeat = 1;
if (args.length >= 1)
count = Integer.parseInt(args[0]);
if (args.length >= 2)
repeat = Integer.parseInt(args[1]);
Map<String, Object> text = new HashMap<String, Object>();
text.put("_data", "2011-08-22 17:30:23 Google 111.222.33.44 GET /search q=cache:xgLxoOQBOoIJ:"
+ "araqneapps.org/+araqneapps&cd=1&hl=en&ct=clnk&source=www.google.com 80 - 123.234.34.45 "
+ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 "
+ "Safari/535.1 404 0 3");
Map<String, Object> map = new HashMap<String, Object>();
map.put("c-ip", "111.222.33.44");
map.put("cs(User-Agent)",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1");
map.put("cs-method", "GET");
map.put("cs-uri-query", "q=cache:xgLxoOQBOoIJ:araqneapps.org/+araqneapps&cd=1&hl=en&ct=clnk&source=www.google.com");
map.put("cs-uri-stem", "/search");
map.put("cs-username", "-");
map.put("date", "2011-08-22");
map.put("s-ip", "123.234.34.45");
map.put("s-port", "80");
map.put("s-sitename", "Google");
map.put("sc-status", "200");
map.put("sc-substatus", "0");
map.put("sc-win32-status", "0");
map.put("time", "17:30:23");
for (int i = 1; i <= repeat; i++) {
context.println("=== Test #" + i + " ===");
benchmark("text", tableName, count, text);
benchmark("map", tableName, count, map);
context.println("");
}
}
private void benchmark(String name, String tableName, int count, Map<String, Object> data) throws InterruptedException {
try {
storage.createTable(new TableSchema(tableName, new StorageConfig("v3p")));
} catch (UnsupportedLogFileTypeException e) {
storage.createTable(new TableSchema(tableName, new StorageConfig("v2")));
}
Log log = new Log(tableName, new Date(), data);
long begin = System.currentTimeMillis();
for (long id = 1; id <= count; id++) {
log.setId(id);
storage.write(log);
}
long end = System.currentTimeMillis();
long time = end - begin;
String timeStr = null;
if (time == 0)
timeStr = "n/a";
else
timeStr = String.format("%d logs/s", count * 1000L / time);
context.println(String.format("%s(write): %d log/%d ms (%s)", name, count, time, timeStr));
begin = System.currentTimeMillis();
try {
LogTraverseCallback.Sink benchSink = new LogTraverseCallback.Sink(0, count) {
@Override
protected void processLogs(List<Log> logs) {
}
};
storage.search(new TableScanRequest(tableName, new Date(0), new Date(), null,
new SimpleLogTraverseCallback(benchSink)));
} catch (InterruptedException e) {
}
end = System.currentTimeMillis();
time = end - begin;
if (time == 0)
timeStr = "n/a";
else
timeStr = String.format("%d logs/s", count * 1000L / time);
context.println(String.format("%s(read): %d log/%d ms (%s)", name, count, time, timeStr));
storage.dropTable(tableName);
}
/**
* @since 1.16.0
*/
public void installedEngines(String[] args) {
context.println("Installed File Engines");
context.println("------------------------");
for (String type : lfsRegistry.getInstalledTypes()) {
context.println(type);
}
}
/**
* @since 1.16.0
*/
@ScriptUsage(description = "uninstall engine. request to unloaded engine will not blocked any more. "
+ "insteads, it will throw unsupported exception", arguments = { @ScriptArgument(name = "engine type", type = "string", description = "engine type name") })
public void uninstallEngine(String[] args) {
lfsRegistry.uninstall(args[0]);
context.println("removed from file engine list");
}
@ScriptUsage(description = "purge log files between specified days", arguments = {
@ScriptArgument(name = "table name", type = "string", description = "table name"),
@ScriptArgument(name = "from", type = "string", description = "yyyyMMdd"),
@ScriptArgument(name = "to", type = "string", description = "yyyyMMdd") })
public void purge(String[] args) throws ParseException {
if (SystemProperty.isEnabled("cc_compliant") ){
context.println("cannot purge the table");
return;
}
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String receivedTableName = args[0];
Date fromDay = df.parse(args[1]);
Date toDay = df.parse(args[2]);
if (fromDay.after(toDay)) {
context.println("invalid date range");
return;
}
PurgePrinter printer = new PurgePrinter();
try {
storage.addEventListener(printer);
Set<String> tableNames = TableWildcardMatcher.apply(new HashSet<String>(tableRegistry.getTableNames()),
receivedTableName);
for (String tableName : tableNames)
storage.purge(tableName, fromDay, toDay);
} finally {
storage.removeEventListener(printer);
}
context.println("completed");
}
private class PurgePrinter implements LogStorageEventListener {
@Override
public void onPurge(String tableName, Date day) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
context.println("purging table " + tableName + " day " + df.format(DateUtil.getDay(day)));
}
@Override
public void onClose(String tableName, Date day) {
}
}
public void _lock(String[] args) throws InterruptedException {
UUID lock = storage.lock(new LockKey("script", args[0], null), args[1], 5, TimeUnit.SECONDS);
if (lock != null)
context.println("locked");
else {
context.printf("failed: %s\n", lockStatusStr(args[0]));
}
}
public void _unlock(String[] args) {
storage.unlock(new LockKey("script", args[0], null), args[1]);
context.printf("unlocked: %s\n", lockStatusStr(args[0]));
}
public void _unlockForce(String[] args) {
storage.unlock(new LockKey(args[0], args[1], null), args[2]);
context.printf("unlocked: %s\n", lockStatusStr(args[1]));
}
}