Created
February 2, 2012 15:33
-
-
Save misterdai/1724012 to your computer and use it in GitHub Desktop.
Adobe ColdFusion, CFML based file that could be called periodically to log session activity for an application. (AdobeCF only, tested on 8.0.1 should work on 9).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
start = Now(); | |
/* | |
=== Session Logger === | |
== ChangeLog == | |
* More config options. | |
* Supports CREATED, ACCESSED, UPDATED, REMOVED. | |
* Detects changes in the log keys and redisplays in log if changed. | |
* Windows newline ending, configurable. | |
* Versioning to help switch between versions / upgrade. | |
* Combines ACCESSED and UPDATED to avoid double entries. | |
* Should detect sessions that have been updated with being accessed. | |
== Installation == | |
Configure this as a scheduled task to run as often as you require. | |
Note that this hasn't been tested against a large amount of sessions. | |
== Notes == | |
* Loops through all sessions for an application | |
* Logs the following: | |
* Any sessions that no longer exist. | |
* Any new sessions. | |
* Any sessions that have been accessed. | |
* Currently there is no log rotation. | |
* Ideally, the log on session access would only occur if values have | |
changed. Requires hash comparison. | |
== Configuration == | |
config.targetApp: This is the application name to session log. | |
config.srvScopeKey: Where this code stores information. | |
config.logKeys: Additional log columns (session metadata / session vals) | |
config.logFile: Path to the log file | |
*/ | |
// config.targetApp | |
config = { | |
version = "0.5", | |
targetApp = "CFTTEST", | |
srvScopeKey = "cft_session_log", | |
logKeys = ["*lastAccessed", "*clientIp", "st.test", 'csv1', 'csv2', 'csv3'], | |
logFile = ExpandPath("./log.txt"), | |
newline = Chr(13) & Chr(10), | |
logActions = { | |
CREATED = true, | |
ACCESSED = true, | |
UPDATED = true, | |
REMOVED = true | |
} | |
}; | |
config.logColLen = ArrayLen(config.logKeys); | |
function LogMsg() { | |
var lc = {}; | |
lc.line = [DateFormat(Now(), "yyyy-mm-dd") & " " & TimeFormat(Now(), "HH:mm:ss")]; | |
lc.argCount = ArrayLen(arguments); | |
for (lc.a = 1; lc.a <= lc.argCount; lc.a++) { | |
if (IsArray(arguments[lc.a])) { | |
lc.elCount = ArrayLen(arguments[lc.a]); | |
for (lc.i = 1; lc.i <= lc.elCount; lc.i++) { | |
ArrayAppend(lc.line, FormatCsv(arguments[lc.a][lc.i])); | |
} | |
} else { | |
ArrayAppend(lc.line, FormatCsv(arguments[lc.a])); | |
} | |
} | |
FileWrite(variables.logHandle, ArrayToList(lc.line) & variables.config.newline); | |
} | |
function FormatCsv(txt) { | |
var result = arguments.txt; | |
// Does the text contain a double quote or comma? | |
if (ReFind('(,|")', result)) { | |
// Double up any double quotes and then surround with them | |
result = '"' & Replace(result, '"', '""', "all") & '"'; | |
} | |
return result; | |
} | |
function GetSessionInfo(session, key) { | |
var lc = {}; | |
lc.result = []; | |
lc.mirror0 = []; | |
for (lc.c = 1; lc.c <= variables.config.logColLen; lc.c++) { | |
lc.col = variables.config.logKeys[lc.c]; | |
switch(lc.col) { | |
case "*lastAccessed": | |
ArrayAppend(lc.result, server[variables.config.srvScopeKey].sessions[key].lastAccessed); | |
break; | |
case "*clientIp"://,*expired,*isNew,*idFromUrl": | |
lc.temp = server[variables.config.srvScopeKey].methods[Replace(lc.col, '*', '')].invoke(arguments.session, lc.mirror0); | |
if (Not StructKeyExists(lc, "temp")) lc.temp = "Unknown"; | |
ArrayAppend(lc.result, lc.temp); | |
break; | |
case '*timeAlive': | |
lc.timeAlive = server[variables.config.srvScopeKey].methods.timeAlive.invoke(arguments.session, lc.mirror0); | |
ArrayAppend(lc.result, DateAdd('s', -lc.timeAlive / 1000, Now())); | |
break; | |
case '*idleTimeout': | |
lc.idleTimeout = server[variables.config.srvScopeKey].methods.idleTimeout.invoke(arguments.session, lc.mirror0); | |
lc.lastAccessed = server[variables.config.srvScopeKey].methods.lastAccessed.invoke(arguments.session, lc.mirror0); | |
ArrayAppend(lc.result, DateAdd('s', lc.idleTimeout, DateAdd('s', -lc.lastAccessed / 1000, Now()))); | |
break; | |
default: | |
// Normal session keys caught here | |
if (Not StructKeyExists(lc, 'table')) { | |
// Grab the session scope, WITHOUT causing the last accessed time to change | |
lc.table = server[variables.config.srvScopeKey].methods.getTable.get(arguments.session); | |
} | |
if (IsDefined("lc.table." & lc.col)) { | |
lc.temp = Evaluate("lc.table." & lc.col); | |
if (Not StructKeyExists(lc, 'temp')) { | |
lc.temp = "NULL"; | |
} else if (Not IsSimpleValue(lc.temp)) { | |
try { | |
lc.temp = "COMPLEX-" & SerializeJson(lc.temp); | |
} catch (Any e) { | |
lc.temp = "COMPLEX-CANNOT-JSON"; | |
} | |
} | |
} else { | |
lc.temp = "NOTFOUND"; | |
} | |
ArrayAppend(lc.result, lc.temp); | |
} | |
} | |
return lc.result; | |
} | |
logHandle = FileOpen(config.logFile, "append"); | |
// Maintain data and useful in the server scope. | |
if (Not StructKeyExists(server, config.srvScopeKey) || StructKeyExists(url, 'reset') | |
|| Not StructKeyExists(server[config.srvScopeKey], 'version') | |
|| server[config.srvScopeKey].version != config.version) { | |
server[config.srvScopeKey] = { | |
version = config.version, | |
lastRun = CreateDate(1970, 1, 1), | |
lastHeaders = "", | |
sessions = {}, | |
jSessTracker = CreateObject("java", "coldfusion.runtime.SessionTracker"), | |
jAppTracker = CreateObject("java", "coldfusion.runtime.ApplicationScopeTracker") | |
}; | |
// Use Java reflection to grab copies of the methods we need | |
mirror = []; | |
class = mirror.getClass().forName("coldfusion.runtime.SessionScope"); | |
server[config.srvScopeKey].methods = { | |
timeAlive = class.getMethod("getElapsedTime", mirror), | |
lastAccessed = class.getMethod("getTimeSinceLastAccess", mirror), | |
idleTimeout = class.getMethod("getMaxInactiveInterval", mirror), | |
expired = class.getMethod("expired", mirror), | |
clientIp = class.getMethod("getClientIp", mirror), | |
idFromUrl = class.getMethod("isIdFromURL", mirror), | |
isNew = class.getMethod("isNew", mirror), | |
getTable = class.getDeclaredField('mTable') | |
}; | |
server[config.srvScopeKey].methods.getTable.setAccessible(true); | |
mirror = []; | |
mirror[1] = CreateObject("java", "java.lang.String").getClass(); | |
server[config.srvScopeKey].methods.getValue = class.getMethod("getValueWIthoutChange", mirror); | |
} | |
if (server[config.srvScopeKey].lastHeaders != ArrayToList(config.logKeys) | |
|| GetFileInfo(config.logFile).size == 0) { | |
FileWrite(logHandle, "timestamp,action,sessionId," & ArrayToList(config.logKeys) & config.newline); | |
server[config.srvScopeKey].lastHeaders = ArrayToList(config.logKeys); | |
} | |
mirror = []; | |
sessions = server[config.srvScopeKey].jSessTracker.getSessionCollection(JavaCast("string", config.targetApp)); | |
keys = StructKeyArray(sessions); | |
sessCount = ArrayLen(keys); | |
// Loop previously seen sessions, check that they still exist | |
for (key in server[config.srvScopeKey].sessions) { | |
if (Not StructKeyExists(sessions, key)) { | |
// Session no longer exists, log and remove | |
if (config.logActions.removed) logMsg(key, 'REMOVED'); | |
StructDelete(server[config.srvScopeKey].sessions, key); | |
} | |
} | |
for (s = 1; s <= sessCount; s++) { | |
key = keys[s]; | |
lastAccessed = DateAdd("s", Fix(-server[config.srvScopeKey].methods.lastAccessed.invoke(sessions[key], mirror) / 1000), Now()); | |
if (Not StructKeyExists(server[config.srvScopeKey].sessions, key)) { | |
// Must be a new session or first logging run | |
server[config.srvScopeKey].sessions[key] = { | |
lastAccessed = lastAccessed, | |
hashcode = server[config.srvScopeKey].methods.getTable.get(sessions[key]).hashcode() | |
}; | |
if (config.logActions.CREATED) logMsg(key, 'CREATED', getSessionInfo(sessions[key], key)); | |
} else { | |
flags = []; | |
if (config.logActions.UPDATED) { | |
hashcode = server[config.srvScopeKey].methods.getTable.get(sessions[key]).hashcode(); | |
if (server[config.srvScopeKey].sessions[key].hashcode != hashcode) { | |
server[config.srvScopeKey].sessions[key].lastAccessed = lastAccessed; | |
server[config.srvScopeKey].sessions[key].hashcode = hashcode; | |
ArrayAppend(flags, 'UPDATED'); | |
} | |
} | |
if (config.logActions.ACCESSED && server[config.srvScopeKey].lastRun <= lastAccessed) { | |
server[config.srvScopeKey].sessions[key].lastAccessed = lastAccessed; | |
ArrayAppend(flags, 'ACCESSED'); | |
} | |
if (ArrayLen(flags) > 0) { | |
logMsg(key, ArrayToList(flags, "+"), getSessionInfo(sessions[key], key)); | |
} | |
} | |
} | |
FileClose(logHandle); | |
/* | |
Update the last log execution time. | |
We use "start" to make sure we don't miss any changes that | |
occurred during the log loop. | |
*/ | |
//server[config.srvScopeKey].lastRun = start; | |
</cfscript> | |
<!---<cfdump var="#server[config.srvScopeKey].lastRun#" /> | |
<cfdump var="#server[config.srvScopeKey].sessions#" /> | |
<cfdump var="#keys#" />---> | |
<cfset server[config.srvScopeKey].lastRun = start /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment