webscout 8.2.7__py3-none-any.whl → 8.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIauto.py +1 -1
- webscout/AIutel.py +298 -249
- webscout/Extra/Act.md +309 -0
- webscout/Extra/GitToolkit/__init__.py +10 -0
- webscout/Extra/GitToolkit/gitapi/README.md +110 -0
- webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
- webscout/Extra/GitToolkit/gitapi/user.py +96 -0
- webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
- webscout/Extra/YTToolkit/README.md +375 -0
- webscout/Extra/YTToolkit/YTdownloader.py +957 -0
- webscout/Extra/YTToolkit/__init__.py +3 -0
- webscout/Extra/YTToolkit/transcriber.py +476 -0
- webscout/Extra/YTToolkit/ytapi/README.md +44 -0
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
- webscout/Extra/YTToolkit/ytapi/https.py +88 -0
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
- webscout/Extra/YTToolkit/ytapi/query.py +40 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
- webscout/Extra/YTToolkit/ytapi/video.py +232 -0
- webscout/Extra/__init__.py +7 -0
- webscout/Extra/autocoder/__init__.py +9 -0
- webscout/Extra/autocoder/autocoder.py +1105 -0
- webscout/Extra/autocoder/autocoder_utiles.py +332 -0
- webscout/Extra/gguf.md +430 -0
- webscout/Extra/gguf.py +684 -0
- webscout/Extra/tempmail/README.md +488 -0
- webscout/Extra/tempmail/__init__.py +28 -0
- webscout/Extra/tempmail/async_utils.py +141 -0
- webscout/Extra/tempmail/base.py +161 -0
- webscout/Extra/tempmail/cli.py +187 -0
- webscout/Extra/tempmail/emailnator.py +84 -0
- webscout/Extra/tempmail/mail_tm.py +361 -0
- webscout/Extra/tempmail/temp_mail_io.py +292 -0
- webscout/Extra/weather.md +281 -0
- webscout/Extra/weather.py +194 -0
- webscout/Extra/weather_ascii.py +76 -0
- webscout/Litlogger/Readme.md +175 -0
- webscout/Litlogger/__init__.py +67 -0
- webscout/Litlogger/core/__init__.py +6 -0
- webscout/Litlogger/core/level.py +23 -0
- webscout/Litlogger/core/logger.py +165 -0
- webscout/Litlogger/handlers/__init__.py +12 -0
- webscout/Litlogger/handlers/console.py +33 -0
- webscout/Litlogger/handlers/file.py +143 -0
- webscout/Litlogger/handlers/network.py +173 -0
- webscout/Litlogger/styles/__init__.py +7 -0
- webscout/Litlogger/styles/colors.py +249 -0
- webscout/Litlogger/styles/formats.py +458 -0
- webscout/Litlogger/styles/text.py +87 -0
- webscout/Litlogger/utils/__init__.py +6 -0
- webscout/Litlogger/utils/detectors.py +153 -0
- webscout/Litlogger/utils/formatters.py +200 -0
- webscout/Provider/AI21.py +177 -0
- webscout/Provider/AISEARCH/DeepFind.py +254 -0
- webscout/Provider/AISEARCH/Perplexity.py +359 -0
- webscout/Provider/AISEARCH/README.md +279 -0
- webscout/Provider/AISEARCH/__init__.py +9 -0
- webscout/Provider/AISEARCH/felo_search.py +228 -0
- webscout/Provider/AISEARCH/genspark_search.py +350 -0
- webscout/Provider/AISEARCH/hika_search.py +198 -0
- webscout/Provider/AISEARCH/iask_search.py +436 -0
- webscout/Provider/AISEARCH/monica_search.py +246 -0
- webscout/Provider/AISEARCH/scira_search.py +324 -0
- webscout/Provider/AISEARCH/webpilotai_search.py +281 -0
- webscout/Provider/Aitopia.py +316 -0
- webscout/Provider/AllenAI.py +440 -0
- webscout/Provider/Andi.py +228 -0
- webscout/Provider/Blackboxai.py +673 -0
- webscout/Provider/ChatGPTClone.py +237 -0
- webscout/Provider/ChatGPTGratis.py +194 -0
- webscout/Provider/ChatSandbox.py +342 -0
- webscout/Provider/Cloudflare.py +324 -0
- webscout/Provider/Cohere.py +208 -0
- webscout/Provider/Deepinfra.py +340 -0
- webscout/Provider/ExaAI.py +261 -0
- webscout/Provider/ExaChat.py +358 -0
- webscout/Provider/Flowith.py +217 -0
- webscout/Provider/FreeGemini.py +250 -0
- webscout/Provider/Gemini.py +169 -0
- webscout/Provider/GithubChat.py +370 -0
- webscout/Provider/GizAI.py +295 -0
- webscout/Provider/Glider.py +225 -0
- webscout/Provider/Groq.py +801 -0
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +206 -0
- webscout/Provider/HeckAI.py +285 -0
- webscout/Provider/HuggingFaceChat.py +469 -0
- webscout/Provider/Hunyuan.py +283 -0
- webscout/Provider/Jadve.py +291 -0
- webscout/Provider/Koboldai.py +384 -0
- webscout/Provider/LambdaChat.py +411 -0
- webscout/Provider/Llama3.py +259 -0
- webscout/Provider/MCPCore.py +315 -0
- webscout/Provider/Marcus.py +198 -0
- webscout/Provider/Nemotron.py +218 -0
- webscout/Provider/Netwrck.py +270 -0
- webscout/Provider/OLLAMA.py +396 -0
- webscout/Provider/OPENAI/BLACKBOXAI.py +735 -0
- webscout/Provider/OPENAI/Cloudflare.py +378 -0
- webscout/Provider/OPENAI/FreeGemini.py +282 -0
- webscout/Provider/OPENAI/NEMOTRON.py +244 -0
- webscout/Provider/OPENAI/README.md +1253 -0
- webscout/Provider/OPENAI/__init__.py +36 -0
- webscout/Provider/OPENAI/ai4chat.py +293 -0
- webscout/Provider/OPENAI/api.py +810 -0
- webscout/Provider/OPENAI/base.py +249 -0
- webscout/Provider/OPENAI/c4ai.py +373 -0
- webscout/Provider/OPENAI/chatgpt.py +556 -0
- webscout/Provider/OPENAI/chatgptclone.py +488 -0
- webscout/Provider/OPENAI/chatsandbox.py +172 -0
- webscout/Provider/OPENAI/deepinfra.py +319 -0
- webscout/Provider/OPENAI/e2b.py +1356 -0
- webscout/Provider/OPENAI/exaai.py +411 -0
- webscout/Provider/OPENAI/exachat.py +443 -0
- webscout/Provider/OPENAI/flowith.py +162 -0
- webscout/Provider/OPENAI/freeaichat.py +359 -0
- webscout/Provider/OPENAI/glider.py +323 -0
- webscout/Provider/OPENAI/groq.py +361 -0
- webscout/Provider/OPENAI/heckai.py +307 -0
- webscout/Provider/OPENAI/llmchatco.py +335 -0
- webscout/Provider/OPENAI/mcpcore.py +383 -0
- webscout/Provider/OPENAI/multichat.py +376 -0
- webscout/Provider/OPENAI/netwrck.py +356 -0
- webscout/Provider/OPENAI/opkfc.py +496 -0
- webscout/Provider/OPENAI/scirachat.py +471 -0
- webscout/Provider/OPENAI/sonus.py +303 -0
- webscout/Provider/OPENAI/standardinput.py +433 -0
- webscout/Provider/OPENAI/textpollinations.py +339 -0
- webscout/Provider/OPENAI/toolbaz.py +413 -0
- webscout/Provider/OPENAI/typefully.py +355 -0
- webscout/Provider/OPENAI/typegpt.py +358 -0
- webscout/Provider/OPENAI/uncovrAI.py +462 -0
- webscout/Provider/OPENAI/utils.py +307 -0
- webscout/Provider/OPENAI/venice.py +425 -0
- webscout/Provider/OPENAI/wisecat.py +381 -0
- webscout/Provider/OPENAI/writecream.py +163 -0
- webscout/Provider/OPENAI/x0gpt.py +378 -0
- webscout/Provider/OPENAI/yep.py +356 -0
- webscout/Provider/OpenGPT.py +209 -0
- webscout/Provider/Openai.py +496 -0
- webscout/Provider/PI.py +429 -0
- webscout/Provider/Perplexitylabs.py +415 -0
- webscout/Provider/QwenLM.py +254 -0
- webscout/Provider/Reka.py +214 -0
- webscout/Provider/StandardInput.py +290 -0
- webscout/Provider/TTI/AiForce/README.md +159 -0
- webscout/Provider/TTI/AiForce/__init__.py +22 -0
- webscout/Provider/TTI/AiForce/async_aiforce.py +224 -0
- webscout/Provider/TTI/AiForce/sync_aiforce.py +245 -0
- webscout/Provider/TTI/FreeAIPlayground/README.md +99 -0
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +181 -0
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -0
- webscout/Provider/TTI/ImgSys/README.md +174 -0
- webscout/Provider/TTI/ImgSys/__init__.py +23 -0
- webscout/Provider/TTI/ImgSys/async_imgsys.py +202 -0
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +195 -0
- webscout/Provider/TTI/MagicStudio/README.md +101 -0
- webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
- webscout/Provider/TTI/Nexra/README.md +155 -0
- webscout/Provider/TTI/Nexra/__init__.py +22 -0
- webscout/Provider/TTI/Nexra/async_nexra.py +286 -0
- webscout/Provider/TTI/Nexra/sync_nexra.py +258 -0
- webscout/Provider/TTI/PollinationsAI/README.md +146 -0
- webscout/Provider/TTI/PollinationsAI/__init__.py +23 -0
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +311 -0
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +265 -0
- webscout/Provider/TTI/README.md +128 -0
- webscout/Provider/TTI/__init__.py +12 -0
- webscout/Provider/TTI/aiarta/README.md +134 -0
- webscout/Provider/TTI/aiarta/__init__.py +2 -0
- webscout/Provider/TTI/aiarta/async_aiarta.py +482 -0
- webscout/Provider/TTI/aiarta/sync_aiarta.py +440 -0
- webscout/Provider/TTI/artbit/README.md +100 -0
- webscout/Provider/TTI/artbit/__init__.py +22 -0
- webscout/Provider/TTI/artbit/async_artbit.py +155 -0
- webscout/Provider/TTI/artbit/sync_artbit.py +148 -0
- webscout/Provider/TTI/fastflux/README.md +129 -0
- webscout/Provider/TTI/fastflux/__init__.py +22 -0
- webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
- webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
- webscout/Provider/TTI/huggingface/README.md +114 -0
- webscout/Provider/TTI/huggingface/__init__.py +22 -0
- webscout/Provider/TTI/huggingface/async_huggingface.py +199 -0
- webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -0
- webscout/Provider/TTI/piclumen/README.md +161 -0
- webscout/Provider/TTI/piclumen/__init__.py +23 -0
- webscout/Provider/TTI/piclumen/async_piclumen.py +268 -0
- webscout/Provider/TTI/piclumen/sync_piclumen.py +233 -0
- webscout/Provider/TTI/pixelmuse/README.md +79 -0
- webscout/Provider/TTI/pixelmuse/__init__.py +4 -0
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +249 -0
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +182 -0
- webscout/Provider/TTI/talkai/README.md +139 -0
- webscout/Provider/TTI/talkai/__init__.py +4 -0
- webscout/Provider/TTI/talkai/async_talkai.py +229 -0
- webscout/Provider/TTI/talkai/sync_talkai.py +207 -0
- webscout/Provider/TTS/README.md +192 -0
- webscout/Provider/TTS/__init__.py +9 -0
- webscout/Provider/TTS/base.py +159 -0
- webscout/Provider/TTS/deepgram.py +156 -0
- webscout/Provider/TTS/elevenlabs.py +111 -0
- webscout/Provider/TTS/gesserit.py +128 -0
- webscout/Provider/TTS/murfai.py +113 -0
- webscout/Provider/TTS/parler.py +111 -0
- webscout/Provider/TTS/speechma.py +580 -0
- webscout/Provider/TTS/sthir.py +94 -0
- webscout/Provider/TTS/streamElements.py +333 -0
- webscout/Provider/TTS/utils.py +280 -0
- webscout/Provider/TeachAnything.py +229 -0
- webscout/Provider/TextPollinationsAI.py +308 -0
- webscout/Provider/TwoAI.py +280 -0
- webscout/Provider/TypliAI.py +305 -0
- webscout/Provider/UNFINISHED/ChatHub.py +209 -0
- webscout/Provider/UNFINISHED/Youchat.py +330 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
- webscout/Provider/UNFINISHED/oivscode.py +351 -0
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
- webscout/Provider/Venice.py +258 -0
- webscout/Provider/VercelAI.py +253 -0
- webscout/Provider/WiseCat.py +233 -0
- webscout/Provider/WrDoChat.py +370 -0
- webscout/Provider/Writecream.py +246 -0
- webscout/Provider/WritingMate.py +269 -0
- webscout/Provider/__init__.py +172 -0
- webscout/Provider/ai4chat.py +149 -0
- webscout/Provider/akashgpt.py +335 -0
- webscout/Provider/asksteve.py +220 -0
- webscout/Provider/cerebras.py +290 -0
- webscout/Provider/chatglm.py +215 -0
- webscout/Provider/cleeai.py +213 -0
- webscout/Provider/copilot.py +425 -0
- webscout/Provider/elmo.py +283 -0
- webscout/Provider/freeaichat.py +285 -0
- webscout/Provider/geminiapi.py +208 -0
- webscout/Provider/granite.py +235 -0
- webscout/Provider/hermes.py +266 -0
- webscout/Provider/julius.py +223 -0
- webscout/Provider/koala.py +170 -0
- webscout/Provider/learnfastai.py +325 -0
- webscout/Provider/llama3mitril.py +215 -0
- webscout/Provider/llmchat.py +258 -0
- webscout/Provider/llmchatco.py +306 -0
- webscout/Provider/lmarena.py +198 -0
- webscout/Provider/meta.py +801 -0
- webscout/Provider/multichat.py +364 -0
- webscout/Provider/samurai.py +223 -0
- webscout/Provider/scira_chat.py +299 -0
- webscout/Provider/scnet.py +243 -0
- webscout/Provider/searchchat.py +292 -0
- webscout/Provider/sonus.py +258 -0
- webscout/Provider/talkai.py +194 -0
- webscout/Provider/toolbaz.py +353 -0
- webscout/Provider/turboseek.py +266 -0
- webscout/Provider/typefully.py +202 -0
- webscout/Provider/typegpt.py +289 -0
- webscout/Provider/uncovr.py +368 -0
- webscout/Provider/x0gpt.py +299 -0
- webscout/Provider/yep.py +389 -0
- webscout/__init__.py +4 -2
- webscout/cli.py +3 -28
- webscout/conversation.py +35 -35
- webscout/litagent/Readme.md +276 -0
- webscout/litagent/__init__.py +29 -0
- webscout/litagent/agent.py +455 -0
- webscout/litagent/constants.py +60 -0
- webscout/litprinter/__init__.py +59 -0
- webscout/scout/README.md +402 -0
- webscout/scout/__init__.py +8 -0
- webscout/scout/core/__init__.py +7 -0
- webscout/scout/core/crawler.py +140 -0
- webscout/scout/core/scout.py +568 -0
- webscout/scout/core/search_result.py +96 -0
- webscout/scout/core/text_analyzer.py +63 -0
- webscout/scout/core/text_utils.py +277 -0
- webscout/scout/core/web_analyzer.py +52 -0
- webscout/scout/element.py +460 -0
- webscout/scout/parsers/__init__.py +69 -0
- webscout/scout/parsers/html5lib_parser.py +172 -0
- webscout/scout/parsers/html_parser.py +236 -0
- webscout/scout/parsers/lxml_parser.py +178 -0
- webscout/scout/utils.py +37 -0
- webscout/swiftcli/Readme.md +323 -0
- webscout/swiftcli/__init__.py +95 -0
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +297 -0
- webscout/swiftcli/core/context.py +104 -0
- webscout/swiftcli/core/group.py +241 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +221 -0
- webscout/swiftcli/decorators/options.py +220 -0
- webscout/swiftcli/decorators/output.py +252 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +135 -0
- webscout/swiftcli/plugins/manager.py +262 -0
- webscout/swiftcli/utils/__init__.py +59 -0
- webscout/swiftcli/utils/formatting.py +252 -0
- webscout/swiftcli/utils/parsing.py +267 -0
- webscout/version.py +1 -1
- webscout/webscout_search.py +2 -182
- webscout/webscout_search_async.py +1 -179
- webscout/zeroart/README.md +89 -0
- webscout/zeroart/__init__.py +135 -0
- webscout/zeroart/base.py +66 -0
- webscout/zeroart/effects.py +101 -0
- webscout/zeroart/fonts.py +1239 -0
- {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/METADATA +115 -60
- webscout-8.2.8.dist-info/RECORD +334 -0
- {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/WHEEL +1 -1
- webscout-8.2.7.dist-info/RECORD +0 -26
- {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/entry_points.txt +0 -0
- {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.7.dist-info → webscout-8.2.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
|
|
2
|
+
class LogFormat:
|
|
3
|
+
# Basic formats with improved styling
|
|
4
|
+
MINIMAL = "│ {time} │ {level} │ {message}"
|
|
5
|
+
STANDARD = "┌─ {time} ─┐\n│ {level} │ {name}: {message}"
|
|
6
|
+
DETAILED = """╭──────────────────────╮
|
|
7
|
+
│ {time}
|
|
8
|
+
│ {level} • {name}
|
|
9
|
+
│ {file}:{line}
|
|
10
|
+
├──────────────────────┤
|
|
11
|
+
│ {message}
|
|
12
|
+
╰──────────────────────╯"""
|
|
13
|
+
SIMPLE = "• {message}"
|
|
14
|
+
COMPACT = "⟦{time}⟧ {level} ⟫ {message}"
|
|
15
|
+
|
|
16
|
+
# Modern Styles with Unicode
|
|
17
|
+
MODERN = """┌────────────────────┐
|
|
18
|
+
│ {time}
|
|
19
|
+
├────────────────────┤
|
|
20
|
+
│ {level} • {name}
|
|
21
|
+
│ {message}
|
|
22
|
+
└────────────────────┘"""
|
|
23
|
+
|
|
24
|
+
MODERN_EMOJI = """╭─ {emoji} {time} ─╮
|
|
25
|
+
│ {level}
|
|
26
|
+
├───────────────────
|
|
27
|
+
│ {message}
|
|
28
|
+
╰───────────────────"""
|
|
29
|
+
|
|
30
|
+
MODERN_CLEAN = """• {time} •
|
|
31
|
+
┌─ {level}
|
|
32
|
+
└→ {message}"""
|
|
33
|
+
|
|
34
|
+
MODERN_BRACKET = """【{time}】
|
|
35
|
+
「{level}」{message}"""
|
|
36
|
+
|
|
37
|
+
MODERN_PLUS = """⊕ {time}
|
|
38
|
+
├─ {level}
|
|
39
|
+
└─ {message}"""
|
|
40
|
+
|
|
41
|
+
MODERN_DOT = """● {time}
|
|
42
|
+
├● {level}
|
|
43
|
+
└● {message}"""
|
|
44
|
+
|
|
45
|
+
MODERN_ARROW = """➤ {time}
|
|
46
|
+
├➤ {level}
|
|
47
|
+
└➤ {message}"""
|
|
48
|
+
|
|
49
|
+
# Boxed Styles
|
|
50
|
+
BOXED = """
|
|
51
|
+
╭─────────────────────╮
|
|
52
|
+
│ [{time}]
|
|
53
|
+
│ {level} - {name}
|
|
54
|
+
│ {message}
|
|
55
|
+
╰─────────────────────╯"""
|
|
56
|
+
|
|
57
|
+
DOUBLE_BOX = """
|
|
58
|
+
╔══════════════════════╗
|
|
59
|
+
║ {level} @ {time}
|
|
60
|
+
║ {name}
|
|
61
|
+
║ {message}
|
|
62
|
+
╚══════════════════════╝"""
|
|
63
|
+
|
|
64
|
+
ROUNDED_BOX = """
|
|
65
|
+
╭──────────────────────╮
|
|
66
|
+
│ {time} • {level}
|
|
67
|
+
├──────────────────────┤
|
|
68
|
+
│ {message}
|
|
69
|
+
╰──────────────────────╯"""
|
|
70
|
+
|
|
71
|
+
RAINBOW_BOX = """
|
|
72
|
+
{level} - {time}
|
|
73
|
+
{name}: {message}
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
HEAVY_BOX = """
|
|
77
|
+
┏━━━━━━━━━━━━━━━━━━━━┓
|
|
78
|
+
┃ {time}
|
|
79
|
+
┃ {level} | {name}
|
|
80
|
+
┃ {message}
|
|
81
|
+
┗━━━━━━━━━━━━━━━━━━━━┛"""
|
|
82
|
+
|
|
83
|
+
DOTTED_BOX = """
|
|
84
|
+
╭┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╮
|
|
85
|
+
┊ {time} | {level}
|
|
86
|
+
┊ {message}
|
|
87
|
+
╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╯"""
|
|
88
|
+
|
|
89
|
+
# Debug & Development
|
|
90
|
+
DEBUG = "[{time}] {level} {name} ({file}:{line}) {message}"
|
|
91
|
+
DEBUG_FULL = """
|
|
92
|
+
┌─ Debug Info ─┐
|
|
93
|
+
│ Time: {time}
|
|
94
|
+
│ Level: {level}
|
|
95
|
+
│ Name: {name}
|
|
96
|
+
│ File: {file}:{line}
|
|
97
|
+
│ Message: {message}
|
|
98
|
+
└──────────────┘"""
|
|
99
|
+
|
|
100
|
+
TRACE = """
|
|
101
|
+
Trace Details:
|
|
102
|
+
Time: {time}
|
|
103
|
+
Level: {level}
|
|
104
|
+
Location: {file}:{line}
|
|
105
|
+
Message: {message}"""
|
|
106
|
+
|
|
107
|
+
DEBUG_COMPACT = "[DBG][{time}] {message} @{file}:{line}"
|
|
108
|
+
DEBUG_EXTENDED = """
|
|
109
|
+
🔍 Debug Information 🔍
|
|
110
|
+
⏰ Time: {time}
|
|
111
|
+
📊 Level: {level}
|
|
112
|
+
📂 File: {file}
|
|
113
|
+
📍 Line: {line}
|
|
114
|
+
💬 Message: {message}
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
# Enhanced Error Formats
|
|
118
|
+
ERROR = """╔══ ERROR ══╗
|
|
119
|
+
║ Time: {time}
|
|
120
|
+
║ Level: {level}
|
|
121
|
+
║ Component: {name}
|
|
122
|
+
╟──────────────
|
|
123
|
+
║ {message}
|
|
124
|
+
╚════════════╝"""
|
|
125
|
+
|
|
126
|
+
ERROR_DETAILED = """┏━━ ALERT ━━┓
|
|
127
|
+
┃ Time: {time}
|
|
128
|
+
┃ Level: {level}
|
|
129
|
+
┃ Component: {name}
|
|
130
|
+
┃ Location: {file}:{line}
|
|
131
|
+
┣━━━━━━━━━━━
|
|
132
|
+
┃ {message}
|
|
133
|
+
┗━━━━━━━━━━━┛"""
|
|
134
|
+
|
|
135
|
+
ERROR_COMPACT = "‼ [{time}] {level}: {message}"
|
|
136
|
+
|
|
137
|
+
ERROR_EMOJI = """❌ ERROR REPORT
|
|
138
|
+
⏰ {time}
|
|
139
|
+
⚠️ {level}
|
|
140
|
+
📝 {message}"""
|
|
141
|
+
|
|
142
|
+
ERROR_BLOCK = """█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
|
|
143
|
+
█ ERROR @ {time}
|
|
144
|
+
█ {level}
|
|
145
|
+
█ {message}
|
|
146
|
+
█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█"""
|
|
147
|
+
|
|
148
|
+
# Status & Progress with Better Visual Hierarchy
|
|
149
|
+
STATUS = """┌─── Status Update ───┐
|
|
150
|
+
│ Time: {time}
|
|
151
|
+
│ Level: {level}
|
|
152
|
+
│ Component: {name}
|
|
153
|
+
├───────────────────┤
|
|
154
|
+
│ {message}
|
|
155
|
+
└───────────────────┘"""
|
|
156
|
+
|
|
157
|
+
PROGRESS = """╭── Progress Report ──╮
|
|
158
|
+
│ Time: {time}
|
|
159
|
+
│ Level: {level}
|
|
160
|
+
├────────────────────
|
|
161
|
+
│ Component: {name}
|
|
162
|
+
│ Status: {message}
|
|
163
|
+
╰────────────────────"""
|
|
164
|
+
|
|
165
|
+
PROGRESS_SIMPLE = "► {time} | {level} | {message}"
|
|
166
|
+
PROGRESS_BAR = "[{progress_bar}] {percentage}% - {message}"
|
|
167
|
+
|
|
168
|
+
# Network & API
|
|
169
|
+
HTTP = """
|
|
170
|
+
API {level} [{time}]
|
|
171
|
+
Endpoint: {name}
|
|
172
|
+
Response: {message}"""
|
|
173
|
+
|
|
174
|
+
REQUEST = """
|
|
175
|
+
→ Incoming Request
|
|
176
|
+
Time: {time}
|
|
177
|
+
Level: {level}
|
|
178
|
+
API: {name}
|
|
179
|
+
Details: {message}"""
|
|
180
|
+
|
|
181
|
+
RESPONSE = """
|
|
182
|
+
← Outgoing Response
|
|
183
|
+
Time: {time}
|
|
184
|
+
Level: {level}
|
|
185
|
+
API: {name}
|
|
186
|
+
Details: {message}"""
|
|
187
|
+
|
|
188
|
+
API_COMPACT = "{method} {url} - {status_code} ({time})"
|
|
189
|
+
API_DETAILED = """┌── API Request ──┐
|
|
190
|
+
│ Time: {time}
|
|
191
|
+
│ Method: {method}
|
|
192
|
+
│ URL: {url}
|
|
193
|
+
├─────────────────
|
|
194
|
+
│ Status: {status_code}
|
|
195
|
+
│ {message}
|
|
196
|
+
└─────────────────"""
|
|
197
|
+
|
|
198
|
+
# System & Metrics
|
|
199
|
+
SYSTEM = """┌─── System Status ───┐
|
|
200
|
+
│ {time}
|
|
201
|
+
├───────────────────────
|
|
202
|
+
│ Component: {name}
|
|
203
|
+
│ Status: {level}
|
|
204
|
+
│ Message: {message}
|
|
205
|
+
└───────────────────────"""
|
|
206
|
+
|
|
207
|
+
METRIC = """┌── Metrics ──┐
|
|
208
|
+
│ Time: {time}
|
|
209
|
+
│ Value: {value}{units}
|
|
210
|
+
│ {message}
|
|
211
|
+
└─────────────"""
|
|
212
|
+
|
|
213
|
+
METRIC_COMPACT = "[METRIC] {name}={value} {units}"
|
|
214
|
+
METRIC_JSON = '{{"metric":"{name}","value":{value},"time":"{time}"}}'
|
|
215
|
+
|
|
216
|
+
# Security & Audit
|
|
217
|
+
SECURITY = """
|
|
218
|
+
Security Event
|
|
219
|
+
Time: {time}
|
|
220
|
+
Level: {level}
|
|
221
|
+
Source: {name}
|
|
222
|
+
Event: {message}"""
|
|
223
|
+
|
|
224
|
+
AUDIT = """
|
|
225
|
+
Audit Log Entry
|
|
226
|
+
Time: {time}
|
|
227
|
+
Level: {level}
|
|
228
|
+
Component: {name}
|
|
229
|
+
Action: {message}"""
|
|
230
|
+
|
|
231
|
+
SECURITY_ALERT = """╔═══ SECURITY ALERT ═══╗
|
|
232
|
+
║ Time: {time}
|
|
233
|
+
║ Level: {level}
|
|
234
|
+
║ Component: {name}
|
|
235
|
+
╟───────────────────────
|
|
236
|
+
║ {message}
|
|
237
|
+
╚═══════════════════════"""
|
|
238
|
+
|
|
239
|
+
# Special Formats
|
|
240
|
+
RAINBOW = " {time} {level} {message}"
|
|
241
|
+
MINIMAL_EMOJI = "{emoji} {message}"
|
|
242
|
+
TIMESTAMP = "{time} {message}"
|
|
243
|
+
COMPONENT = "[{name}] {message}"
|
|
244
|
+
HASH = "#{hash} | {time} | {message}"
|
|
245
|
+
TAG = "@{tag} | {time} | {message}"
|
|
246
|
+
|
|
247
|
+
# Data Formats
|
|
248
|
+
JSON = '{{"time":"{time}","level":"{level}","name":"{name}","message":"{message}"}}'
|
|
249
|
+
JSON_PRETTY = """{
|
|
250
|
+
"timestamp": "{time}",
|
|
251
|
+
"level": "{level}",
|
|
252
|
+
"component": "{name}",
|
|
253
|
+
"message": "{message}"
|
|
254
|
+
}"""
|
|
255
|
+
|
|
256
|
+
XML = """<log>
|
|
257
|
+
<time>{time}</time>
|
|
258
|
+
<level>{level}</level>
|
|
259
|
+
<name>{name}</name>
|
|
260
|
+
<message>{message}</message>
|
|
261
|
+
</log>"""
|
|
262
|
+
|
|
263
|
+
YAML = """---
|
|
264
|
+
timestamp: {time}
|
|
265
|
+
level: {level}
|
|
266
|
+
component: {name}
|
|
267
|
+
details:
|
|
268
|
+
message: {message}
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
# Modern Documentation Formats
|
|
272
|
+
MARKDOWN = """## Log Entry
|
|
273
|
+
**Time:** `{time}`
|
|
274
|
+
**Level:** `{level}`
|
|
275
|
+
**Component:** `{name}`
|
|
276
|
+
|
|
277
|
+
> {message}
|
|
278
|
+
---"""
|
|
279
|
+
|
|
280
|
+
RST = """
|
|
281
|
+
Log Entry
|
|
282
|
+
=========
|
|
283
|
+
:Time: {time}
|
|
284
|
+
:Level: {level}
|
|
285
|
+
:Component: {name}
|
|
286
|
+
:Message: {message}
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
HTML = """<div class="log-entry">
|
|
290
|
+
<div class="log-header">
|
|
291
|
+
<span class="time">{time}</span>
|
|
292
|
+
<span class="level">{level}</span>
|
|
293
|
+
</div>
|
|
294
|
+
<div class="log-body">
|
|
295
|
+
<span class="name">{name}</span>
|
|
296
|
+
<span class="message">{message}</span>
|
|
297
|
+
</div>
|
|
298
|
+
</div>"""
|
|
299
|
+
|
|
300
|
+
# Rich Console Formats
|
|
301
|
+
RICH = """╭── {name} ──╮
|
|
302
|
+
│ {time} │ {level_colored}
|
|
303
|
+
├──────────┴───────────
|
|
304
|
+
│ {message}
|
|
305
|
+
│ {context}{exception}
|
|
306
|
+
╰────────────────────"""
|
|
307
|
+
|
|
308
|
+
RICH_DETAILED = """╔══════════════════════════╗
|
|
309
|
+
║ {name}
|
|
310
|
+
╟──────────────────────────
|
|
311
|
+
║ Time: {time}
|
|
312
|
+
║ Level: {level_colored}
|
|
313
|
+
║ Thread: {thread_info}
|
|
314
|
+
╟──────────────────────────
|
|
315
|
+
║ {message}
|
|
316
|
+
║ {context}{exception}
|
|
317
|
+
╚══════════════════════════╝"""
|
|
318
|
+
|
|
319
|
+
RICH_MINIMAL = "│ {time} │ {level_colored} │ {message}"
|
|
320
|
+
|
|
321
|
+
RICH_COMPACT = "⟦{time}⟧ {level_colored} [{name}] ⟫ {message}"
|
|
322
|
+
|
|
323
|
+
# Template registry
|
|
324
|
+
TEMPLATES = {
|
|
325
|
+
# Basic formats
|
|
326
|
+
"minimal": MINIMAL,
|
|
327
|
+
"standard": STANDARD,
|
|
328
|
+
"detailed": DETAILED,
|
|
329
|
+
"simple": SIMPLE,
|
|
330
|
+
"compact": COMPACT,
|
|
331
|
+
|
|
332
|
+
# Modern styles
|
|
333
|
+
"modern": MODERN,
|
|
334
|
+
"modern_emoji": MODERN_EMOJI,
|
|
335
|
+
"modern_clean": MODERN_CLEAN,
|
|
336
|
+
"modern_bracket": MODERN_BRACKET,
|
|
337
|
+
"modern_plus": MODERN_PLUS,
|
|
338
|
+
"modern_dot": MODERN_DOT,
|
|
339
|
+
"modern_arrow": MODERN_ARROW,
|
|
340
|
+
|
|
341
|
+
# Boxed styles
|
|
342
|
+
"boxed": BOXED,
|
|
343
|
+
"double_box": DOUBLE_BOX,
|
|
344
|
+
"rounded_box": ROUNDED_BOX,
|
|
345
|
+
"rainbow_box": RAINBOW_BOX,
|
|
346
|
+
"heavy_box": HEAVY_BOX,
|
|
347
|
+
"dotted_box": DOTTED_BOX,
|
|
348
|
+
|
|
349
|
+
# Debug formats
|
|
350
|
+
"debug": DEBUG,
|
|
351
|
+
"debug_full": DEBUG_FULL,
|
|
352
|
+
"trace": TRACE,
|
|
353
|
+
"debug_compact": DEBUG_COMPACT,
|
|
354
|
+
"debug_extended": DEBUG_EXTENDED,
|
|
355
|
+
|
|
356
|
+
# Error formats
|
|
357
|
+
"error": ERROR,
|
|
358
|
+
"error_detailed": ERROR_DETAILED,
|
|
359
|
+
"error_compact": ERROR_COMPACT,
|
|
360
|
+
"error_emoji": ERROR_EMOJI,
|
|
361
|
+
"error_block": ERROR_BLOCK,
|
|
362
|
+
|
|
363
|
+
# Status formats
|
|
364
|
+
"status": STATUS,
|
|
365
|
+
"progress": PROGRESS,
|
|
366
|
+
"progress_simple": PROGRESS_SIMPLE,
|
|
367
|
+
"progress_bar": PROGRESS_BAR,
|
|
368
|
+
|
|
369
|
+
# Network formats
|
|
370
|
+
"http": HTTP,
|
|
371
|
+
"request": REQUEST,
|
|
372
|
+
"response": RESPONSE,
|
|
373
|
+
"api_compact": API_COMPACT,
|
|
374
|
+
"api_detailed": API_DETAILED,
|
|
375
|
+
|
|
376
|
+
# System formats
|
|
377
|
+
"system": SYSTEM,
|
|
378
|
+
"metric": METRIC,
|
|
379
|
+
"metric_compact": METRIC_COMPACT,
|
|
380
|
+
"metric_json": METRIC_JSON,
|
|
381
|
+
|
|
382
|
+
# Security formats
|
|
383
|
+
"security": SECURITY,
|
|
384
|
+
"audit": AUDIT,
|
|
385
|
+
"security_alert": SECURITY_ALERT,
|
|
386
|
+
|
|
387
|
+
# Special formats
|
|
388
|
+
"rainbow": RAINBOW,
|
|
389
|
+
"minimal_emoji": MINIMAL_EMOJI,
|
|
390
|
+
"timestamp": TIMESTAMP,
|
|
391
|
+
"component": COMPONENT,
|
|
392
|
+
"hash": HASH,
|
|
393
|
+
"tag": TAG,
|
|
394
|
+
|
|
395
|
+
# Data formats
|
|
396
|
+
"json": JSON,
|
|
397
|
+
"json_pretty": JSON_PRETTY,
|
|
398
|
+
"xml": XML,
|
|
399
|
+
"yaml": YAML,
|
|
400
|
+
|
|
401
|
+
# Documentation formats
|
|
402
|
+
"markdown": MARKDOWN,
|
|
403
|
+
"rst": RST,
|
|
404
|
+
"html": HTML,
|
|
405
|
+
|
|
406
|
+
# Rich-like formats
|
|
407
|
+
"rich": RICH,
|
|
408
|
+
"rich_detailed": RICH_DETAILED,
|
|
409
|
+
"rich_minimal": RICH_MINIMAL,
|
|
410
|
+
"rich_compact": RICH_COMPACT,
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@staticmethod
|
|
414
|
+
def create_custom(template: str) -> str:
|
|
415
|
+
"""Create a custom log format template."""
|
|
416
|
+
try:
|
|
417
|
+
# Test if the template is valid by formatting with dummy data
|
|
418
|
+
dummy_data = {
|
|
419
|
+
"time": "2024-01-01 00:00:00",
|
|
420
|
+
"level": "INFO",
|
|
421
|
+
"name": "test",
|
|
422
|
+
"message": "test message",
|
|
423
|
+
"file": "test.py",
|
|
424
|
+
"line": 1,
|
|
425
|
+
"emoji": "✨",
|
|
426
|
+
"progress_bar": "==========",
|
|
427
|
+
"percentage": 100,
|
|
428
|
+
"method": "GET",
|
|
429
|
+
"url": "/test",
|
|
430
|
+
"status_code": 200,
|
|
431
|
+
"value": 42,
|
|
432
|
+
"units": "ms",
|
|
433
|
+
"hash": "abc123",
|
|
434
|
+
"tag": "test"
|
|
435
|
+
}
|
|
436
|
+
template.format(**dummy_data)
|
|
437
|
+
return template
|
|
438
|
+
except KeyError as e:
|
|
439
|
+
raise ValueError(f"Invalid format template. Missing key: {e}")
|
|
440
|
+
except Exception as e:
|
|
441
|
+
raise ValueError(f"Invalid format template: {e}")
|
|
442
|
+
|
|
443
|
+
@staticmethod
|
|
444
|
+
def get_format(format_name: str) -> str:
|
|
445
|
+
"""Get a predefined format template by name."""
|
|
446
|
+
if format_name in LogFormat.TEMPLATES:
|
|
447
|
+
return LogFormat.TEMPLATES[format_name]
|
|
448
|
+
raise ValueError(f"Unknown format: {format_name}")
|
|
449
|
+
|
|
450
|
+
@staticmethod
|
|
451
|
+
def register_template(name: str, template: str):
|
|
452
|
+
"""Register a new format template."""
|
|
453
|
+
if name in LogFormat.TEMPLATES:
|
|
454
|
+
raise ValueError(f"Format template '{name}' already exists")
|
|
455
|
+
|
|
456
|
+
# Validate template before registering
|
|
457
|
+
LogFormat.create_custom(template)
|
|
458
|
+
LogFormat.TEMPLATES[name] = template
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
class TextStyle:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def wrap(text: str, width: int = 80, indent: str = "") -> str:
|
|
7
|
+
"""Wrap text to specified width with optional indentation."""
|
|
8
|
+
return textwrap.fill(text, width=width, initial_indent=indent,
|
|
9
|
+
subsequent_indent=indent)
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def truncate(text: str, max_length: int, suffix: str = "...") -> str:
|
|
13
|
+
"""Truncate text to maximum length with suffix."""
|
|
14
|
+
if len(text) <= max_length:
|
|
15
|
+
return text
|
|
16
|
+
return text[:max_length - len(suffix)] + suffix
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def pad(text: str, width: int, align: str = "left",
|
|
20
|
+
fill_char: str = " ") -> str:
|
|
21
|
+
"""Pad text to specified width with alignment."""
|
|
22
|
+
if align == "left":
|
|
23
|
+
return text.ljust(width, fill_char)
|
|
24
|
+
elif align == "right":
|
|
25
|
+
return text.rjust(width, fill_char)
|
|
26
|
+
elif align == "center":
|
|
27
|
+
return text.center(width, fill_char)
|
|
28
|
+
raise ValueError(f"Invalid alignment: {align}")
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def indent(text: str, prefix: str = " ", predicate: Optional[callable] = None) -> str:
|
|
32
|
+
"""Indent text lines with prefix based on optional predicate."""
|
|
33
|
+
lines = text.splitlines(True)
|
|
34
|
+
if predicate is None:
|
|
35
|
+
predicate = bool
|
|
36
|
+
|
|
37
|
+
def should_indent(line):
|
|
38
|
+
return predicate(line)
|
|
39
|
+
|
|
40
|
+
return "".join(prefix + line if should_indent(line) else line
|
|
41
|
+
for line in lines)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def highlight(text: str, substring: str, color: str) -> str:
|
|
45
|
+
"""Highlight substring within text using ANSI color codes."""
|
|
46
|
+
from ..styles.colors import LogColors
|
|
47
|
+
if not substring:
|
|
48
|
+
return text
|
|
49
|
+
|
|
50
|
+
parts = text.split(substring)
|
|
51
|
+
highlighted = f"{color}{substring}{LogColors.RESET}"
|
|
52
|
+
return highlighted.join(parts)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def table(headers: list, rows: list, padding: int = 1) -> str:
|
|
56
|
+
"""Create a formatted table with headers and rows."""
|
|
57
|
+
if not rows:
|
|
58
|
+
return ""
|
|
59
|
+
|
|
60
|
+
# Calculate column widths
|
|
61
|
+
widths = [max(len(str(row[i])) for row in [headers] + rows)
|
|
62
|
+
for i in range(len(headers))]
|
|
63
|
+
|
|
64
|
+
# Create separator line
|
|
65
|
+
separator = "+" + "+".join("-" * (w + 2 * padding) for w in widths) + "+"
|
|
66
|
+
|
|
67
|
+
# Format header
|
|
68
|
+
header = "|" + "|".join(
|
|
69
|
+
str(h).center(w + 2 * padding) for h, w in zip(headers, widths)
|
|
70
|
+
) + "|"
|
|
71
|
+
|
|
72
|
+
# Format rows
|
|
73
|
+
formatted_rows = []
|
|
74
|
+
for row in rows:
|
|
75
|
+
formatted_row = "|" + "|".join(
|
|
76
|
+
str(cell).ljust(w + 2 * padding) for cell, w in zip(row, widths)
|
|
77
|
+
) + "|"
|
|
78
|
+
formatted_rows.append(formatted_row)
|
|
79
|
+
|
|
80
|
+
# Combine all parts
|
|
81
|
+
return "\n".join([
|
|
82
|
+
separator,
|
|
83
|
+
header,
|
|
84
|
+
separator,
|
|
85
|
+
*formatted_rows,
|
|
86
|
+
separator
|
|
87
|
+
])
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from ..core.level import LogLevel
|
|
5
|
+
|
|
6
|
+
class LevelDetector:
|
|
7
|
+
"""Utility class for intelligent log level detection."""
|
|
8
|
+
|
|
9
|
+
# Common patterns indicating error conditions
|
|
10
|
+
ERROR_PATTERNS = [
|
|
11
|
+
r"error",
|
|
12
|
+
r"exception",
|
|
13
|
+
r"failed",
|
|
14
|
+
r"failure",
|
|
15
|
+
r"traceback",
|
|
16
|
+
r"crash",
|
|
17
|
+
r"fatal",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Common patterns indicating warning conditions
|
|
21
|
+
WARNING_PATTERNS = [
|
|
22
|
+
r"warning",
|
|
23
|
+
r"warn",
|
|
24
|
+
r"deprecated",
|
|
25
|
+
r"might",
|
|
26
|
+
r"consider",
|
|
27
|
+
r"attention",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# Common patterns indicating debug information
|
|
31
|
+
DEBUG_PATTERNS = [
|
|
32
|
+
r"debug",
|
|
33
|
+
r"trace",
|
|
34
|
+
r"verbose",
|
|
35
|
+
r"detail",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def detect_level(cls, message: str, exception: Optional[Exception] = None) -> LogLevel:
|
|
40
|
+
"""
|
|
41
|
+
Detect appropriate log level from message content and context.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
message: Log message to analyze
|
|
45
|
+
exception: Optional exception object
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Detected LogLevel
|
|
49
|
+
"""
|
|
50
|
+
# If there's an exception, it's at least ERROR
|
|
51
|
+
if exception is not None:
|
|
52
|
+
if isinstance(exception, (SystemExit, KeyboardInterrupt)):
|
|
53
|
+
return LogLevel.CRITICAL
|
|
54
|
+
return LogLevel.ERROR
|
|
55
|
+
|
|
56
|
+
# Convert to lowercase for pattern matching
|
|
57
|
+
message_lower = message.lower()
|
|
58
|
+
|
|
59
|
+
# Check for error patterns
|
|
60
|
+
for pattern in cls.ERROR_PATTERNS:
|
|
61
|
+
if re.search(pattern, message_lower):
|
|
62
|
+
return LogLevel.ERROR
|
|
63
|
+
|
|
64
|
+
# Check for warning patterns
|
|
65
|
+
for pattern in cls.WARNING_PATTERNS:
|
|
66
|
+
if re.search(pattern, message_lower):
|
|
67
|
+
return LogLevel.WARNING
|
|
68
|
+
|
|
69
|
+
# Check for debug patterns
|
|
70
|
+
for pattern in cls.DEBUG_PATTERNS:
|
|
71
|
+
if re.search(pattern, message_lower):
|
|
72
|
+
return LogLevel.DEBUG
|
|
73
|
+
|
|
74
|
+
# Default to INFO
|
|
75
|
+
return LogLevel.INFO
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def detect_level_from_exception(cls, exc_type, exc_value, exc_tb) -> LogLevel:
|
|
79
|
+
"""
|
|
80
|
+
Detect log level from exception information.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
exc_type: Exception type
|
|
84
|
+
exc_value: Exception value
|
|
85
|
+
exc_tb: Exception traceback
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Detected LogLevel
|
|
89
|
+
"""
|
|
90
|
+
# Critical errors that should terminate execution
|
|
91
|
+
if exc_type in (SystemExit, KeyboardInterrupt, MemoryError):
|
|
92
|
+
return LogLevel.CRITICAL
|
|
93
|
+
|
|
94
|
+
# Built-in exceptions that indicate programming errors
|
|
95
|
+
if exc_type in (
|
|
96
|
+
SyntaxError,
|
|
97
|
+
IndentationError,
|
|
98
|
+
NameError,
|
|
99
|
+
TypeError,
|
|
100
|
+
AttributeError
|
|
101
|
+
):
|
|
102
|
+
return LogLevel.ERROR
|
|
103
|
+
|
|
104
|
+
# Runtime errors that might be recoverable
|
|
105
|
+
if exc_type in (
|
|
106
|
+
ValueError,
|
|
107
|
+
KeyError,
|
|
108
|
+
IOError,
|
|
109
|
+
OSError,
|
|
110
|
+
ConnectionError
|
|
111
|
+
):
|
|
112
|
+
return LogLevel.WARNING
|
|
113
|
+
|
|
114
|
+
# Default to ERROR for unknown exception types
|
|
115
|
+
return LogLevel.ERROR
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def detect_level_from_frame(cls, frame_depth: int = 1) -> LogLevel:
|
|
119
|
+
"""
|
|
120
|
+
Detect log level by analyzing the call stack.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
frame_depth: How many frames up to look (1 = caller's frame)
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Detected LogLevel
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
frame = sys._getframe(frame_depth)
|
|
130
|
+
|
|
131
|
+
# Check function/method name
|
|
132
|
+
code = frame.f_code
|
|
133
|
+
func_name = code.co_name.lower()
|
|
134
|
+
|
|
135
|
+
if any(p in func_name for p in ["error", "fail", "crash"]):
|
|
136
|
+
return LogLevel.ERROR
|
|
137
|
+
if any(p in func_name for p in ["warn"]):
|
|
138
|
+
return LogLevel.WARNING
|
|
139
|
+
if any(p in func_name for p in ["debug", "trace"]):
|
|
140
|
+
return LogLevel.DEBUG
|
|
141
|
+
|
|
142
|
+
# Check local variables for exceptions
|
|
143
|
+
locals_dict = frame.f_locals
|
|
144
|
+
for value in locals_dict.values():
|
|
145
|
+
if isinstance(value, Exception):
|
|
146
|
+
return cls.detect_level_from_exception(
|
|
147
|
+
type(value), value, value.__traceback__
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return LogLevel.INFO
|
|
151
|
+
|
|
152
|
+
except Exception:
|
|
153
|
+
return LogLevel.INFO # Default if detection fails
|