java - 记录对 Java servlet 的访问
问题描述
我目前正在使用 Servlet 开发 Java Web 应用程序。我需要做的是每次访问网站时都记录到一个文件中。为此,我使用了过滤器。到目前为止,我已经可以将所有内容打印到控制台。
我现在需要做的是将其存储到一个文件中,该文件最多包含 10.000 个条目,最长可达 30 天(如果达到最大条目,则在写入新条目时替换最旧的条目)。
我怎样才能做到这一点?
PS:我不能为这个作业使用数据库
编辑:我没有使用网络框架。我可以使用日志框架。
解决方案
因此,这个问题实际上促使我调查是否有任何流行的日志记录框架实际上可以按要求完成任务。
虽然大多数基于文件大小和日期/时间滚动日志,但没有一个简单的方法可以根据日志文件中的条目来滚动日志。此外,现有的日志框架通常将每天(有时是较小的时间单位)存储在自己的单独文件中,从而根据日期/时间进行有效清理。
由于需要单个文件中的最大行数,因此需要将整个文件读入内存(非常低效!)。当过去和现在的所有内容都写入单个文件时,删除较旧的条目需要解析每一行以获取写入条目的日期/时间(同样,效率低下!)。
下面是一个简单的程序来证明可以做到这一点,但是这种方法存在一些严重的问题:
- 不是线程安全的(如果两个线程试图同时读/写一个条目,一个会被破坏并且消息会被跳过)
- 啜饮是不好的(一万个条目很多:服务器可以把所有这些啜饮到内存中吗?)
这可能适用于玩具项目、演示或学校作业。
这不适用于生产应用程序,或者实际上不适合多人同时使用的网络上的任何东西。
简而言之,如果您尝试将在 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);
}
}
推荐阅读
- php - PHP Laravel 传递模型作为函数参数
- python - Mac 终端窗口中的 (base) (null)
- java - 线程局部变量
- nginx - nginx反向代理,多个远程应用程序和相同的端口
- javascript - 在 Chrome 开发人员工具中,如何禁用刷新元素周围的突出显示?
- c - 谁能指出为什么我有这个错误'malloc.c:2369: sysmalloc: Assertion'?
- c - 如何扩展指针
在飞镖? - python - 是否可以在 Python unittest 中进行子测试“预期失败”?
- azure - Azure 数据工厂中的 *.servicebus.windows.net 用于什么?
- python - Django - 更新主字段时修改外键字段