Skip to content

Commit

Permalink
Configure event log name and fallback on failure to read (oshi#1045)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbwiddis authored Nov 14, 2019
1 parent 6823af1 commit 9325493
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static oshi.software.os.OSService.State.OTHER;
import static oshi.software.os.OSService.State.RUNNING;
import static oshi.software.os.OSService.State.STOPPED;
import static oshi.util.Memoizer.memoize;

import java.io.File;
import java.util.ArrayList;
Expand All @@ -36,6 +37,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -86,6 +89,7 @@
import oshi.software.os.OSService;
import oshi.software.os.OSService.State;
import oshi.software.os.OperatingSystem;
import oshi.util.GlobalConfig;
import oshi.util.ParseUtil;
import oshi.util.platform.windows.PerfCounterQuery;
import oshi.util.platform.windows.PerfCounterWildcardQuery;
Expand All @@ -100,6 +104,12 @@ public class WindowsOperatingSystem extends AbstractOperatingSystem {
private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
private static final boolean IS_WINDOWS7_OR_GREATER = VersionHelpers.IsWindows7OrGreater();

/**
* Windows event log name
*/
private static Supplier<String> systemLog = memoize(WindowsOperatingSystem::querySystemLog,
TimeUnit.HOURS.toNanos(1));

private static final long BOOTTIME = querySystemBootTime();

private static final HkeyPerformanceData HKEY_PERFORMANCE_DATA;
Expand Down Expand Up @@ -639,30 +649,41 @@ public long getSystemBootTime() {
}

private static long querySystemBootTime() {
// Get the most recent boot event (ID 12) from the Event log. If Windows "Fast
// Startup" is enabled we may not see event 12, so also check for most recent ID
// 6005 (Event log startup) as a reasonably close backup.
long event6005Time = 0L;
EventLogIterator iter = new EventLogIterator(null, "System", WinNT.EVENTLOG_BACKWARDS_READ);
while (iter.hasNext()) {
EventLogRecord record = iter.next();
if (record.getStatusCode() == 12) {
// Event 12 is system boot. We want this value unless we find two 6005 events
// first (may occur with Fast Boot)
return record.getRecord().TimeGenerated.longValue();
} else if (record.getStatusCode() == 6005) {
// If we already found one, this means we've found a second one without finding
// an event 12. Return the latest one.
String eventLog = systemLog.get();
if (eventLog != null) {
try {
EventLogIterator iter = new EventLogIterator(null, eventLog, WinNT.EVENTLOG_BACKWARDS_READ);
// Get the most recent boot event (ID 12) from the Event log. If Windows "Fast
// Startup" is enabled we may not see event 12, so also check for most recent ID
// 6005 (Event log startup) as a reasonably close backup.
long event6005Time = 0L;
while (iter.hasNext()) {
EventLogRecord record = iter.next();
if (record.getStatusCode() == 12) {
// Event 12 is system boot. We want this value unless we find two 6005 events
// first (may occur with Fast Boot)
return record.getRecord().TimeGenerated.longValue();
} else if (record.getStatusCode() == 6005) {
// If we already found one, this means we've found a second one without finding
// an event 12. Return the latest one.
if (event6005Time > 0) {
return event6005Time;
}
// First 6005; tentatively assign
event6005Time = record.getRecord().TimeGenerated.longValue();
}
}
// Only one 6005 found, return
if (event6005Time > 0) {
return event6005Time;
}
// First 6005; tentatively assign
event6005Time = record.getRecord().TimeGenerated.longValue();
} catch (Win32Exception e) {
LOG.warn("Can't open event log \"{}\".", eventLog);
}
}
// If we get this far, event log reading has failed. Subtract up time from
// current time as a reasonable proxy.
return event6005Time > 0 ? event6005Time : System.currentTimeMillis() / 1000L - querySystemUptime();
// If we get this far, event log reading has failed, either from no log or no
// startup times. Subtract up time from current time as a reasonable proxy.
return System.currentTimeMillis() / 1000L - querySystemUptime();
}

@Override
Expand Down Expand Up @@ -728,6 +749,22 @@ public OSService[] getServices() {
}
}

private static String querySystemLog() {
String systemLog = GlobalConfig.get("oshi.os.windows.eventlog", "System");
if (systemLog.isEmpty()) {
// Use faster boot time approximation
return null;
}
// Check whether it works
HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, systemLog);
if (h == null) {
LOG.warn("Unable to open configured system Event log \"{}\". Calculating boot time from uptime.",
systemLog);
return null;
}
return systemLog;
}

enum OSVersionProperty {
Version, ProductType, BuildNumber, CSDVersion, SuiteMask;
}
Expand Down
17 changes: 17 additions & 0 deletions oshi-core/src/main/resources/oshi.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ oshi.util.proc.path=/proc
# Default is -1, no timeout
oshi.util.wmi.timeout=-1

# The name of the System event log containing bootup event IDs 12 and 6005. If a
# non-empty invalid log name is specified, the name "Application" will be used.
#
# This is used for a one-time calculation of system boot time that should be
# consistent across process runs regardless of sleep/hibernate cycles, at
# the small cost of ~250ms latency reading upon WindowsOperatingSystem
# initialization.
#
# If the specified log is the empty string, or doesn't contain a bootup event,
# boot time will be calculated by subtracting up time from current time. This
# may vary by up to a millisecond between program executions and does not
# properly account for sleep/hibernate cycles, but is fast and may be preferred
# if only approximate boot time is desired.
#
# Default is System
oshi.os.windows.eventlog=System

# Memoizer default expiration in milliseconds (return values will be cached this long)
# Must be positive (negative value will never refresh)
# Should be less than 1 second
Expand Down

0 comments on commit 9325493

Please sign in to comment.