记录对 Java servlet 的访问
Logging access to Java servlet
我目前正在使用 Servlet 开发 Java Web 应用程序。我需要做的是将对网站的每次访问都记录到一个文件中。为此,我使用了过滤器。到目前为止,我已经到了可以将所有内容打印到控制台的地步。
我现在需要做的是将其存储到一个最多包含 10.000 个条目的文件中,最长可达 30 天(如果达到最大条目数,则在写入新条目时将替换最旧的条目)。
我该怎么做?
P.S: 我不能为这个作业使用数据库
编辑:我没有使用网络框架。我可以使用日志框架。
所以,这个问题实际上促使我调查是否有任何流行的日志记录框架实际上可以按要求完成任务。
虽然大多数人根据文件大小和 date/time 进行滚动日志,但其中 none 有一种简单的方法可以根据日志文件中的条目进行滚动日志。此外,现有的日志记录框架通常将每一天(有时更小的时间单位)存储在它们自己的单独文件中,从而根据 date/time.
进行高效清理
由于单个文件中的最大行数要求,这需要将整个文件读入内存(非常低效!)。当过去和现在的所有内容都被写入单个文件时,删除较旧的条目需要解析写入该条目的 date/time 的每一行(同样,效率低下!)。
下面是一个简单的程序来演示可以做到这一点,但是这种方法存在一些严重的问题:
- 不是线程安全的(如果两个线程同时尝试 read/write 一个条目,一个将被破坏并且消息将被跳过)
- Slurping 不好(一万个条目很多:服务器可以将所有这些都 Slurping 到内存中吗?)
这可能适合玩具项目、演示或学校作业。
这不适合生产应用程序,或者网络上任何一次多人使用的应用程序。
简而言之,如果您尝试将您在 Internet 上找到的手工程序用于其他人依赖的关键任务应用程序,您将得到您应得的.
public static void main(final String[] args) throws Exception
{
final File logFile = new File("C:/", "EverythingInOneBigGiant.log");
final int maxDays = 30;
final int maxEntries = 10000;
while (true)
{
// Just log the current time for this example, also makes parsing real simple
final String msg = Instant.now().toString();
slurpAndParse(logFile, msg, maxDays, maxEntries);
// Wait a moment, before writing another entry
Thread.sleep(750);
}
}
private static void slurpAndParse(final File f, final String msg, final int maxDays, final int maxEntries)
throws Exception
{
// Slurp entire file into this buffer (possibly very large!)
// Could crash your server if you run out of memory
final StringBuffer sb = new StringBuffer();
if (f.exists() && f.isFile())
{
final LocalDateTime now = LocalDateTime.now();
final long totalLineCount = Files.lines(Paths.get(f.getAbsolutePath())).count();
final long startAtLine = (totalLineCount < maxEntries ? 0 : (totalLineCount - maxEntries) + 1);
long currentLineCount = 0;
try (final BufferedReader br = new BufferedReader(new FileReader(f)))
{
String line;
while (null != (line = br.readLine()))
{
// Ignore all lines before the start counter
if (currentLineCount < startAtLine)
{
++currentLineCount;
continue;
}
// Parsing log data... while writing to the same log... ugh... how hideous
final LocalDateTime lineDate = LocalDateTime.parse(line, DateTimeFormatter.ISO_ZONED_DATE_TIME);
final Duration timeBetween = Duration.between(lineDate, now);
// ... or maybe just use Math.abs() here? I defer to the date/time buffs
final long dayDiff = (timeBetween.isNegative() ? timeBetween.negated() : timeBetween).toDays();
// Only accept lines less than the max age in days
if (dayDiff <= maxDays)
{
sb.append(line);
sb.append(System.lineSeparator());
}
}
}
}
System.out.println(msg);
// Add the new log entry
sb.append(msg);
sb.append(System.lineSeparator());
writeLog(f, sb.toString());
}
private static void writeLog(final File f, final String content) throws IOException
{
try (final Writer out = new FileWriter(f))
{
out.write(content);
}
}
我目前正在使用 Servlet 开发 Java Web 应用程序。我需要做的是将对网站的每次访问都记录到一个文件中。为此,我使用了过滤器。到目前为止,我已经到了可以将所有内容打印到控制台的地步。
我现在需要做的是将其存储到一个最多包含 10.000 个条目的文件中,最长可达 30 天(如果达到最大条目数,则在写入新条目时将替换最旧的条目)。
我该怎么做?
P.S: 我不能为这个作业使用数据库
编辑:我没有使用网络框架。我可以使用日志框架。
所以,这个问题实际上促使我调查是否有任何流行的日志记录框架实际上可以按要求完成任务。
虽然大多数人根据文件大小和 date/time 进行滚动日志,但其中 none 有一种简单的方法可以根据日志文件中的条目进行滚动日志。此外,现有的日志记录框架通常将每一天(有时更小的时间单位)存储在它们自己的单独文件中,从而根据 date/time.
进行高效清理由于单个文件中的最大行数要求,这需要将整个文件读入内存(非常低效!)。当过去和现在的所有内容都被写入单个文件时,删除较旧的条目需要解析写入该条目的 date/time 的每一行(同样,效率低下!)。
下面是一个简单的程序来演示可以做到这一点,但是这种方法存在一些严重的问题:
- 不是线程安全的(如果两个线程同时尝试 read/write 一个条目,一个将被破坏并且消息将被跳过)
- Slurping 不好(一万个条目很多:服务器可以将所有这些都 Slurping 到内存中吗?)
这可能适合玩具项目、演示或学校作业。
这不适合生产应用程序,或者网络上任何一次多人使用的应用程序。
简而言之,如果您尝试将您在 Internet 上找到的手工程序用于其他人依赖的关键任务应用程序,您将得到您应得的.
public static void main(final String[] args) throws Exception
{
final File logFile = new File("C:/", "EverythingInOneBigGiant.log");
final int maxDays = 30;
final int maxEntries = 10000;
while (true)
{
// Just log the current time for this example, also makes parsing real simple
final String msg = Instant.now().toString();
slurpAndParse(logFile, msg, maxDays, maxEntries);
// Wait a moment, before writing another entry
Thread.sleep(750);
}
}
private static void slurpAndParse(final File f, final String msg, final int maxDays, final int maxEntries)
throws Exception
{
// Slurp entire file into this buffer (possibly very large!)
// Could crash your server if you run out of memory
final StringBuffer sb = new StringBuffer();
if (f.exists() && f.isFile())
{
final LocalDateTime now = LocalDateTime.now();
final long totalLineCount = Files.lines(Paths.get(f.getAbsolutePath())).count();
final long startAtLine = (totalLineCount < maxEntries ? 0 : (totalLineCount - maxEntries) + 1);
long currentLineCount = 0;
try (final BufferedReader br = new BufferedReader(new FileReader(f)))
{
String line;
while (null != (line = br.readLine()))
{
// Ignore all lines before the start counter
if (currentLineCount < startAtLine)
{
++currentLineCount;
continue;
}
// Parsing log data... while writing to the same log... ugh... how hideous
final LocalDateTime lineDate = LocalDateTime.parse(line, DateTimeFormatter.ISO_ZONED_DATE_TIME);
final Duration timeBetween = Duration.between(lineDate, now);
// ... or maybe just use Math.abs() here? I defer to the date/time buffs
final long dayDiff = (timeBetween.isNegative() ? timeBetween.negated() : timeBetween).toDays();
// Only accept lines less than the max age in days
if (dayDiff <= maxDays)
{
sb.append(line);
sb.append(System.lineSeparator());
}
}
}
}
System.out.println(msg);
// Add the new log entry
sb.append(msg);
sb.append(System.lineSeparator());
writeLog(f, sb.toString());
}
private static void writeLog(final File f, final String content) throws IOException
{
try (final Writer out = new FileWriter(f))
{
out.write(content);
}
}