Here's my AutoHotKey solution for quick and easy journaling +auto logging of what you're doing

Sorry for the long title :slight_smile:

So, here goes: one of the small and hacky tools I’ve built for my Obsidian workflow is a simple “easy logger”. I’m sharing it here in case someone finds it useful, but be warned: I’m no dev, I’m only fumbling around when I’m creating such stuff, and although this works, it’s a hacky approach which I’m sure someone could pull of much, much better.

However, it works (for me), and it might also work for someone else. So, here goes.

  1. Download and install AutoHotKey. Sorry, Windows-only (and one of the reasons I haven’t jumped to Linux - yet).
  2. Right-click somewhere and create a new, blank AHK file.
  3. Edit it in your favorite text editor and add the following in it, then save it:
;___________________________________________________________________________________________________


; TimeLogger
; ----------

; User-configurable variables:
; ----------------------------
AppLoggingRate = 10 ; Rate for capturing active window titles - number in seconds
LogPath = ENTER-PATH-WHERE-YOU-KEEP-YOUR-JOURNALING-NOTES-IN-OBSIDIAN'S-VAULT. (Full path please. And yes, erase EVERYTHING on this line after the equals sign and add the path).
AppLogFile = yes ; Use separate AppLog file instead of a single AppLog/Journal file. Yes/No.
SectionTitles = no ; Add H5 headings before every entry.
FirstRun = 0 ; Change to 1 if you want to be automatically asked for an initial log entry after startup.


; Non-configurable stuff:
; -----------------------
; (Please, don't touch)

LastActiveWindow = 
AppLogSwitch = 1
JournalSwitch = 1
RunTime = 0
SleepTime := AppLoggingRate * 1000


; Actual script:
; --------------
	
	Gui, TimeLogger: Font, s12
	Gui, TimeLogger: Add, Edit, vLoggerContent x10 y10 w500 h600,
	Gui, TimeLogger: Font
	Gui, TimeLogger: Add, Button, gLogSave x10 y620 w500 h50, &Save Log Entry


	Gui +LastFound +OwnDialogs +AlwaysOnTop
	InputBox, LoggerRate, TimeLogger Ultimate Hyper Rainbow Edition, When do you want me to check on you?`n`n(time range in minutes)



	Loop
	{
		JournalTime := LoggerRate * 60000
		RunTime := RunTime + SleepTime
		
		Sleep, %SleepTime%
		
		WinGetActiveTitle, ActiveWindow
		StoreActiveWindow = %ActiveWindow%
		
		If ActiveWindow != %LastActiveWindow%
		{
			FormatTime, LogTime,, HH:mm:ss
			FormatTime, LogFilename, , yyyy-MM-dd
			LogWindow := Regexreplace(ActiveWindow, "[^a-zA-Z0-9]", " ")
			
			If AppLogFile = yes
				{
					LogFilename = %LogFilename%_AppLog.md
				}
			Else
				{
					LogFilename = %LogFilename%.md
				}
				
			LogFile = %LogPath%%LogFilename%
			
			
			if (AppLogSwitch = 1) or (SectionTitles = yes)
				{
					FileContent = `n##### AppLog`n%LogTime% - %LogWindow%`n`n- - -`n
				}
			else
				{
					FileContent = `n%LogTime% - %LogWindow%`n`n- - -`n
				}
				
			AppLogSwitch = 0
			JournalSwitch = 1
			sleep, 20

			FileAppend, %FileContent%, %LogFile%
		}
		
		
		
		If (RunTime = JournalTime) or (FirstRun = 1)
		{
			InputBox, LoggerExcuse, TimeLogger Ultimate Hyper Rainbow Edition, GOTCHA - what are you doing?`n`n`n(Manual Journaling: Windows Key + J)`n(Stop logging/Exit TimeLogger: Shift + Windows Key + L)
			sleep, 20
			FormatTime, LogTime,, HH:mm:ss
			;FormatTime, LogFilename, , yyyy-MM-dd_dddd
			FormatTime, LogFilename, , yyyy-MM-dd
			LogWindow := Regexreplace(ActiveWindow, "[^a-zA-Z0-9]", " ")
			LogFilename = %LogFilename%.md
			LogFile = %LogPath%%LogFilename%
			
			If SectionTitles = yes
				{
					FileContent = `n- - -`n##### AutoJournal`n%LogTime% - %LoggerExcuse%`n`n- - -`n
				}
			Else
				{
					FileContent = `n- - -`n%LogTime% - %LoggerExcuse%`n`n- - -`n
				}
			sleep, 20
			RunTime = 0
			AppLogSwitch = 1
			JournalSwitch = 0
			FirstRun = 0

			FileAppend, %FileContent%, %LogFile%
		}
		WinActivate, %StoreActiveWindow%
		LastActiveWindow = %ActiveWindow%
	}
		

WriteJournal:
		FormatTime, LogTime,, HH:mm:ss
		;FormatTime, LogFilename, , yyyy-MM-dd_dddd
		FormatTime, LogFilename, , yyyy-MM-dd
		WinGetActiveTitle, ActiveWindow
		StoreActiveWindow = %ActiveWindow%

		LogWindow := Regexreplace(ActiveWindow, "[^a-zA-Z0-9]", " ")
		LogFilename = %LogFilename%.md
		LogFile = %LogPath%%LogFilename%
		If SectionTitles = yes
				{
					FileContent = `n- - -`n##### Journal`n%LogTime% - %LoggerContent%`n`n- - -`n
				}
		Else
				{
					FileContent = `n%LogTime% - %LoggerContent%`n`n- - -`n
				}
		FileAppend, %FileContent%, %LogFile%
		WinActivate, %StoreActiveWindow%
return

LogSave:
	Gui, TimeLogger: Submit, Hide
	GuiControlGet, LoggerContent
	sleep, 20
	GoSub, WriteJournal
	GuiControl, TimeLogger:, LoggerContent, 
return

#J::
	Gui, TimeLogger: Show
return

#+l::
		ExitApp
return

ExitApp
  1. Use a shortcut to launch the script. You can create a shortcut icon on your desktop, right-click on it, and add a “Shortcut key” that will launch the script. Sorry for not implementing another way, for I’m actually launching this through another AutoHotKey script.

  2. Search for the line “FormatTime, LogFilename, , yyyy-MM-dd”. If you’re using dates in Obsidian, as you’ve probably guessed by now, this sets the formatting of the time, that’s also used as the note’s filename. It’s compatible as-it-is with the popular (AFAIK) approach in Obsidian that uses a “2021-10-21” format for daily notes names. However, it should be easy to tweak it if you’re using a Zettelkasten approach, or anything else for that matter. Note that the above should use typical time-formatting syntax, and not anything else (like your pet’s name). You should set it to use the same time convention as your daily notes, though. I’ll explain why later.

  3. Search for the line “LogFilename = %LogFilename%_AppLog.md”. You can safely change the “_AppLog.md” part to anything you wish. Those are extra files that are created in the same folder as your daily notes, but with an “_AppLog.md” added after the date in their name. You can safely change that to, say, “___Fluffy_The_Poodle.md”. I’ll also explain “what they’re for” later.

  4. I also suggest you keep AppLogFile set to yes (search for “AppLogFile = yes”). AppLogs can have A LOT of entries, and destroy your daily notes with fluff.

With all that out of the way, and semi-understandable notes in the script itself, it’s time for…

The “Later”

So, what does this do, and why could it be helpful to you (as it is for me)?

The way the script is currently set up allows you to quickly jolt down notes in your daily note WITHOUT having to activate Obsidian and visit the particular note. Let’s say you saw the Sad Keanu meme and realized you’re sad, too.

  1. Launch it.
  2. Enter a timeframe in minutes.
  3. Press Windows Key + J at any time.
  4. Type “I’m saaaaaad (and loooooonely)” in the window that pops-up.
  5. Press ALT + S to append your profound thought to your daily note.
  6. Press Windows Key + J again whenever you want to add something else to your daily note, or press Shift + Windows + L to exit the app.

However, that’s not all.

The reason for entering a time when you first launch the script is to keep yourself accountable. The script will pop-up a window after this time passes, asking you what you’re up to, to force you to journal. Something. Anything.

Still, for extra accountability, we have the AppLogs. This is where it gets more interesting.

I’m using AutoHotKey’s ability to detect window changes and window names in the script, to do precisely that. So, every 10 seconds (you can change that from “AppLoggingRate = 10”) the script will:

  • Check if the current active window’s title is different compared to the previous logged entry.
  • If it is, add it to the “DATE_AppLog.md” file, right next to your similarly-named Daily note.
  • Rinse, repeat.

This way you’ll also have a log of every single window you’ve lingered on for more than 10 seconds.

Feel free to take the script, tweak it, extend it, remix it, if you find it useful. I’d appreciate it if you paged me about it, though (for I’d like trying out even better approaches than my sorry and hacky attempt at easier journaling/time-logging).

PS: Remember to change the “LogPath =” to the actual, full path to your Daily Notes folder in Obsidian’s Vault. Feel free to play around with the rest of the options (like switching “SectionTitles = no” to “yes”, although I think it ends up making the daily note feel more crammed with stuff).

PS2: Awesome console. The PlayStation 2, that is. Oh, wait. No, that’s not what I wanted to say… Er…

PS2-Proper: If you don’t like the purty lines and syntax of the notes, tweak the "FileContent = " bits. Log once or twice with the defaults, then check out those values and I’m pretty sure you’ll understand theyr syntax (and how to tweak it to your liking).

2 Likes