net.oneandone.stool.util.Logging Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main Show documentation
Show all versions of main Show documentation
Stool's main component. Java Library, cli, setup code.
/**
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* 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 net.oneandone.stool.util;
import net.oneandone.sushi.fs.Settings;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.io.MultiOutputStream;
import net.oneandone.sushi.io.PrefixWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Logging {
private static final String EXTENSION = ".log";
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd");
public static Logging forHome(FileNode home) throws IOException {
return create(home.join("logs"), "stool");
}
public static Logging create(FileNode dir, String name) throws IOException {
String date;
String prefix;
String id;
Logging result;
date = DATE_FORMAT.format(LocalDate.now());
prefix = date + ".";
id = prefix + Integer.toString(id(dir, prefix));
result = new Logging(id, dir.join(name + "-" + date + EXTENSION));
return result;
}
/** unique id for this invocation */
public final String id;
private final FileNode file;
private final String user;
private String stageId;
private String stageName;
public Logging(String id, FileNode file) throws IOException {
this(id, file, System.getProperty("user.name"));
}
public Logging(String id, FileNode file, String user) throws IOException {
this.id = id;
this.file = file;
this.user = user;
setStage("", "");
if (!file.exists()) {
file.writeBytes();
Files.stoolFile(file);
}
}
public void setStage(String id, String name) {
stageId = id;
stageName = name;
}
public void log(String logger, String message) {
char c;
try (Writer writer = file.newAppender()) {
writer.append(LogEntry.TIME_FMT.format(LocalDateTime.now())).append('|');
writer.append(id).append('|');
writer.append(logger).append('|');
writer.append(user).append('|');
writer.append(stageId).append('|');
writer.append(stageName).append('|');
for (int i = 0, max = message.length(); i < max; i++) {
c = message.charAt(i);
switch (c) {
case '\r':
writer.append("\\r");
break;
case '\n':
writer.append("\\n");
break;
case '\\':
writer.append("\\\\");
break;
default:
writer.append(c);
break;
}
}
writer.append('\n');
} catch (IOException e) {
e.printStackTrace();
}
}
public void error(String message, Throwable throwable) {
log("ERROR", message);
// TODO: throwable is ignored
}
public PrintWriter writer(OutputStream stream, String logger) {
PrintWriter result;
try {
result = new PrintWriter(new OutputStreamWriter(MultiOutputStream.createTeeStream(stream, new LogOutputStream(this, logger)), Settings.UTF_8), true);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
// empty prefix is replaced by stage commands when iterating multiple stages:
result = new PrefixWriter(result);
return result;
}
//--
/**
* Unique id starting with 1 every day, bumped for every invocation.
*/
private static int id(FileNode varRun, String prefix) throws IOException {
int retries;
FileNode lock;
FileNode file;
int id;
String str;
retries = 0;
while (true) {
lock = varRun.join("id.lock");
try {
lock.mkfile();
break;
} catch (IOException e) {
retries++;
if (retries > 10) {
throw new IOException("cannot create " + lock);
}
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
break;
}
}
}
try {
file = varRun.join("id");
if (!file.exists()) {
id = 1;
touch(file);
} else {
str = file.readString();
if (str.startsWith(prefix)) {
id = Integer.parseInt(str.substring(prefix.length())) + 1;
} else {
id = 1;
}
}
file.writeString(prefix + id);
return id;
} finally {
lock.deleteFile();
}
}
private static FileNode touch(FileNode file) throws IOException {
if (!file.exists()) {
file.mkfile();
file.setPermissions("rw-rw-r--");
}
return file;
}
private FileNode directory() {
return file.getParent();
}
/** @return alle COMMAND Log entries operating on the specified stage */
public List stageCommands(String stageId) throws Exception {
LogEntry entry;
Map commands;
LogEntry command;
List result;
result = new ArrayList<>();
commands = new HashMap<>();
try (LogReader reader = LogReader.create(directory())) {
while (true) {
entry = reader.next();
if (entry == null) {
break;
}
if (entry.logger.equals("COMMAND")) {
if (commands.put(entry.id, entry) != null) {
throw new IllegalStateException("duplicate id: " + entry.id);
}
}
if (entry.stageId.equals(stageId)) {
command = commands.remove(entry.id);
if (command != null) {
result.add(command);
}
}
}
}
return result;
}
public List info(String stageId, String id) throws Exception {
LogEntry entry;
List result;
result = new ArrayList<>();
try (LogReader reader = LogReader.create(directory())) {
while (true) {
entry = reader.next();
if (entry == null) {
break;
}
if (entry.id.equals(id) && entry.stageId.equals(stageId)) {
result.add(entry);
}
}
}
return result;
}
public String getUser() {
return user;
}
public void rotate() throws IOException {
long daymillies = 1000l * 60 * 60 * 24;
long manydaysmillies = daymillies * 90;
long now;
now = System.currentTimeMillis();
for (FileNode f : file.getParent().find("*.log")) {
if (now - f.getLastModified() > daymillies) {
f.gzip(f.getParent().join(f.getName() + ".gz"));
f.deleteFile();
}
}
for (FileNode f : file.getParent().find("*.log.gz")) {
if (now - f.getLastModified() > manydaysmillies) {
f.deleteFile();
}
}
}
}