setiastrosuitepro 1.6.1__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 setiastrosuitepro might be problematic. Click here for more details.
- setiastro/__init__.py +2 -0
- setiastro/data/SASP_data.fits +0 -0
- setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
- setiastro/data/catalogs/astrobin_filters.csv +890 -0
- setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
- setiastro/data/catalogs/cali2.csv +63 -0
- setiastro/data/catalogs/cali2color.csv +65 -0
- setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
- setiastro/data/catalogs/celestial_catalog.csv +24031 -0
- setiastro/data/catalogs/detected_stars.csv +24784 -0
- setiastro/data/catalogs/fits_header_data.csv +46 -0
- setiastro/data/catalogs/test.csv +8 -0
- setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
- setiastro/images/Astro_Spikes.png +0 -0
- setiastro/images/HRDiagram.png +0 -0
- setiastro/images/LExtract.png +0 -0
- setiastro/images/LInsert.png +0 -0
- setiastro/images/Oxygenation-atm-2.svg.png +0 -0
- setiastro/images/RGB080604.png +0 -0
- setiastro/images/abeicon.png +0 -0
- setiastro/images/aberration.png +0 -0
- setiastro/images/andromedatry.png +0 -0
- setiastro/images/andromedatry_satellited.png +0 -0
- setiastro/images/annotated.png +0 -0
- setiastro/images/aperture.png +0 -0
- setiastro/images/astrosuite.ico +0 -0
- setiastro/images/astrosuite.png +0 -0
- setiastro/images/astrosuitepro.icns +0 -0
- setiastro/images/astrosuitepro.ico +0 -0
- setiastro/images/astrosuitepro.png +0 -0
- setiastro/images/background.png +0 -0
- setiastro/images/background2.png +0 -0
- setiastro/images/benchmark.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
- setiastro/images/blaster.png +0 -0
- setiastro/images/blink.png +0 -0
- setiastro/images/clahe.png +0 -0
- setiastro/images/collage.png +0 -0
- setiastro/images/colorwheel.png +0 -0
- setiastro/images/contsub.png +0 -0
- setiastro/images/convo.png +0 -0
- setiastro/images/copyslot.png +0 -0
- setiastro/images/cosmic.png +0 -0
- setiastro/images/cosmicsat.png +0 -0
- setiastro/images/crop1.png +0 -0
- setiastro/images/cropicon.png +0 -0
- setiastro/images/curves.png +0 -0
- setiastro/images/cvs.png +0 -0
- setiastro/images/debayer.png +0 -0
- setiastro/images/denoise_cnn_custom.png +0 -0
- setiastro/images/denoise_cnn_graph.png +0 -0
- setiastro/images/disk.png +0 -0
- setiastro/images/dse.png +0 -0
- setiastro/images/exoicon.png +0 -0
- setiastro/images/eye.png +0 -0
- setiastro/images/fliphorizontal.png +0 -0
- setiastro/images/flipvertical.png +0 -0
- setiastro/images/font.png +0 -0
- setiastro/images/freqsep.png +0 -0
- setiastro/images/functionbundle.png +0 -0
- setiastro/images/graxpert.png +0 -0
- setiastro/images/green.png +0 -0
- setiastro/images/gridicon.png +0 -0
- setiastro/images/halo.png +0 -0
- setiastro/images/hdr.png +0 -0
- setiastro/images/histogram.png +0 -0
- setiastro/images/hubble.png +0 -0
- setiastro/images/imagecombine.png +0 -0
- setiastro/images/invert.png +0 -0
- setiastro/images/isophote.png +0 -0
- setiastro/images/isophote_demo_figure.png +0 -0
- setiastro/images/isophote_demo_image.png +0 -0
- setiastro/images/isophote_demo_model.png +0 -0
- setiastro/images/isophote_demo_residual.png +0 -0
- setiastro/images/jwstpupil.png +0 -0
- setiastro/images/linearfit.png +0 -0
- setiastro/images/livestacking.png +0 -0
- setiastro/images/mask.png +0 -0
- setiastro/images/maskapply.png +0 -0
- setiastro/images/maskcreate.png +0 -0
- setiastro/images/maskremove.png +0 -0
- setiastro/images/morpho.png +0 -0
- setiastro/images/mosaic.png +0 -0
- setiastro/images/multiscale_decomp.png +0 -0
- setiastro/images/nbtorgb.png +0 -0
- setiastro/images/neutral.png +0 -0
- setiastro/images/nuke.png +0 -0
- setiastro/images/openfile.png +0 -0
- setiastro/images/pedestal.png +0 -0
- setiastro/images/pen.png +0 -0
- setiastro/images/pixelmath.png +0 -0
- setiastro/images/platesolve.png +0 -0
- setiastro/images/ppp.png +0 -0
- setiastro/images/pro.png +0 -0
- setiastro/images/project.png +0 -0
- setiastro/images/psf.png +0 -0
- setiastro/images/redo.png +0 -0
- setiastro/images/redoicon.png +0 -0
- setiastro/images/rescale.png +0 -0
- setiastro/images/rgbalign.png +0 -0
- setiastro/images/rgbcombo.png +0 -0
- setiastro/images/rgbextract.png +0 -0
- setiastro/images/rotate180.png +0 -0
- setiastro/images/rotateclockwise.png +0 -0
- setiastro/images/rotatecounterclockwise.png +0 -0
- setiastro/images/satellite.png +0 -0
- setiastro/images/script.png +0 -0
- setiastro/images/selectivecolor.png +0 -0
- setiastro/images/simbad.png +0 -0
- setiastro/images/slot0.png +0 -0
- setiastro/images/slot1.png +0 -0
- setiastro/images/slot2.png +0 -0
- setiastro/images/slot3.png +0 -0
- setiastro/images/slot4.png +0 -0
- setiastro/images/slot5.png +0 -0
- setiastro/images/slot6.png +0 -0
- setiastro/images/slot7.png +0 -0
- setiastro/images/slot8.png +0 -0
- setiastro/images/slot9.png +0 -0
- setiastro/images/spcc.png +0 -0
- setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
- setiastro/images/spinner.gif +0 -0
- setiastro/images/stacking.png +0 -0
- setiastro/images/staradd.png +0 -0
- setiastro/images/staralign.png +0 -0
- setiastro/images/starnet.png +0 -0
- setiastro/images/starregistration.png +0 -0
- setiastro/images/starspike.png +0 -0
- setiastro/images/starstretch.png +0 -0
- setiastro/images/statstretch.png +0 -0
- setiastro/images/supernova.png +0 -0
- setiastro/images/uhs.png +0 -0
- setiastro/images/undoicon.png +0 -0
- setiastro/images/upscale.png +0 -0
- setiastro/images/viewbundle.png +0 -0
- setiastro/images/whitebalance.png +0 -0
- setiastro/images/wimi_icon_256x256.png +0 -0
- setiastro/images/wimilogo.png +0 -0
- setiastro/images/wims.png +0 -0
- setiastro/images/wrench_icon.png +0 -0
- setiastro/images/xisfliberator.png +0 -0
- setiastro/saspro/__init__.py +20 -0
- setiastro/saspro/__main__.py +809 -0
- setiastro/saspro/_generated/__init__.py +7 -0
- setiastro/saspro/_generated/build_info.py +2 -0
- setiastro/saspro/abe.py +1295 -0
- setiastro/saspro/abe_preset.py +196 -0
- setiastro/saspro/aberration_ai.py +694 -0
- setiastro/saspro/aberration_ai_preset.py +224 -0
- setiastro/saspro/accel_installer.py +218 -0
- setiastro/saspro/accel_workers.py +30 -0
- setiastro/saspro/add_stars.py +621 -0
- setiastro/saspro/astrobin_exporter.py +1007 -0
- setiastro/saspro/astrospike.py +153 -0
- setiastro/saspro/astrospike_python.py +1839 -0
- setiastro/saspro/autostretch.py +196 -0
- setiastro/saspro/backgroundneutral.py +560 -0
- setiastro/saspro/batch_convert.py +325 -0
- setiastro/saspro/batch_renamer.py +519 -0
- setiastro/saspro/blemish_blaster.py +488 -0
- setiastro/saspro/blink_comparator_pro.py +2926 -0
- setiastro/saspro/bundles.py +61 -0
- setiastro/saspro/bundles_dock.py +114 -0
- setiastro/saspro/cheat_sheet.py +178 -0
- setiastro/saspro/clahe.py +342 -0
- setiastro/saspro/comet_stacking.py +1377 -0
- setiastro/saspro/common_tr.py +107 -0
- setiastro/saspro/config.py +38 -0
- setiastro/saspro/config_bootstrap.py +40 -0
- setiastro/saspro/config_manager.py +316 -0
- setiastro/saspro/continuum_subtract.py +1617 -0
- setiastro/saspro/convo.py +1397 -0
- setiastro/saspro/convo_preset.py +414 -0
- setiastro/saspro/copyastro.py +187 -0
- setiastro/saspro/cosmicclarity.py +1564 -0
- setiastro/saspro/cosmicclarity_preset.py +407 -0
- setiastro/saspro/crop_dialog_pro.py +956 -0
- setiastro/saspro/crop_preset.py +189 -0
- setiastro/saspro/curve_editor_pro.py +2544 -0
- setiastro/saspro/curves_preset.py +375 -0
- setiastro/saspro/debayer.py +670 -0
- setiastro/saspro/debug_utils.py +29 -0
- setiastro/saspro/dnd_mime.py +35 -0
- setiastro/saspro/doc_manager.py +2641 -0
- setiastro/saspro/exoplanet_detector.py +2166 -0
- setiastro/saspro/file_utils.py +284 -0
- setiastro/saspro/fitsmodifier.py +745 -0
- setiastro/saspro/fix_bom.py +32 -0
- setiastro/saspro/free_torch_memory.py +48 -0
- setiastro/saspro/frequency_separation.py +1343 -0
- setiastro/saspro/function_bundle.py +1594 -0
- setiastro/saspro/generate_translations.py +2378 -0
- setiastro/saspro/ghs_dialog_pro.py +660 -0
- setiastro/saspro/ghs_preset.py +284 -0
- setiastro/saspro/graxpert.py +634 -0
- setiastro/saspro/graxpert_preset.py +287 -0
- setiastro/saspro/gui/__init__.py +0 -0
- setiastro/saspro/gui/main_window.py +8567 -0
- setiastro/saspro/gui/mixins/__init__.py +33 -0
- setiastro/saspro/gui/mixins/dock_mixin.py +263 -0
- setiastro/saspro/gui/mixins/file_mixin.py +443 -0
- setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
- setiastro/saspro/gui/mixins/header_mixin.py +441 -0
- setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
- setiastro/saspro/gui/mixins/menu_mixin.py +361 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +1457 -0
- setiastro/saspro/gui/mixins/update_mixin.py +309 -0
- setiastro/saspro/gui/mixins/view_mixin.py +435 -0
- setiastro/saspro/halobgon.py +462 -0
- setiastro/saspro/header_viewer.py +448 -0
- setiastro/saspro/headless_utils.py +88 -0
- setiastro/saspro/histogram.py +753 -0
- setiastro/saspro/history_explorer.py +939 -0
- setiastro/saspro/i18n.py +156 -0
- setiastro/saspro/image_combine.py +414 -0
- setiastro/saspro/image_peeker_pro.py +1601 -0
- setiastro/saspro/imageops/__init__.py +37 -0
- setiastro/saspro/imageops/mdi_snap.py +292 -0
- setiastro/saspro/imageops/scnr.py +36 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
- setiastro/saspro/imageops/stretch.py +244 -0
- setiastro/saspro/isophote.py +1179 -0
- setiastro/saspro/layers.py +208 -0
- setiastro/saspro/layers_dock.py +714 -0
- setiastro/saspro/lazy_imports.py +193 -0
- setiastro/saspro/legacy/__init__.py +2 -0
- setiastro/saspro/legacy/image_manager.py +2226 -0
- setiastro/saspro/legacy/numba_utils.py +3659 -0
- setiastro/saspro/legacy/xisf.py +1071 -0
- setiastro/saspro/linear_fit.py +534 -0
- setiastro/saspro/live_stacking.py +1830 -0
- setiastro/saspro/log_bus.py +5 -0
- setiastro/saspro/logging_config.py +460 -0
- setiastro/saspro/luminancerecombine.py +309 -0
- setiastro/saspro/main_helpers.py +201 -0
- setiastro/saspro/mask_creation.py +928 -0
- setiastro/saspro/masks_core.py +56 -0
- setiastro/saspro/mdi_widgets.py +353 -0
- setiastro/saspro/memory_utils.py +666 -0
- setiastro/saspro/metadata_patcher.py +75 -0
- setiastro/saspro/mfdeconv.py +3826 -0
- setiastro/saspro/mfdeconv_earlystop.py +71 -0
- setiastro/saspro/mfdeconvcudnn.py +3263 -0
- setiastro/saspro/mfdeconvsport.py +2382 -0
- setiastro/saspro/minorbodycatalog.py +567 -0
- setiastro/saspro/morphology.py +382 -0
- setiastro/saspro/multiscale_decomp.py +1290 -0
- setiastro/saspro/nbtorgb_stars.py +531 -0
- setiastro/saspro/numba_utils.py +3044 -0
- setiastro/saspro/numba_warmup.py +141 -0
- setiastro/saspro/ops/__init__.py +9 -0
- setiastro/saspro/ops/command_help_dialog.py +623 -0
- setiastro/saspro/ops/command_runner.py +217 -0
- setiastro/saspro/ops/commands.py +1594 -0
- setiastro/saspro/ops/script_editor.py +1102 -0
- setiastro/saspro/ops/scripts.py +1413 -0
- setiastro/saspro/ops/settings.py +679 -0
- setiastro/saspro/parallel_utils.py +554 -0
- setiastro/saspro/pedestal.py +121 -0
- setiastro/saspro/perfect_palette_picker.py +1070 -0
- setiastro/saspro/pipeline.py +110 -0
- setiastro/saspro/pixelmath.py +1600 -0
- setiastro/saspro/plate_solver.py +2444 -0
- setiastro/saspro/project_io.py +797 -0
- setiastro/saspro/psf_utils.py +136 -0
- setiastro/saspro/psf_viewer.py +549 -0
- setiastro/saspro/pyi_rthook_astroquery.py +95 -0
- setiastro/saspro/remove_green.py +314 -0
- setiastro/saspro/remove_stars.py +1625 -0
- setiastro/saspro/remove_stars_preset.py +404 -0
- setiastro/saspro/resources.py +477 -0
- setiastro/saspro/rgb_combination.py +207 -0
- setiastro/saspro/rgb_extract.py +19 -0
- setiastro/saspro/rgbalign.py +723 -0
- setiastro/saspro/runtime_imports.py +7 -0
- setiastro/saspro/runtime_torch.py +754 -0
- setiastro/saspro/save_options.py +72 -0
- setiastro/saspro/selective_color.py +1552 -0
- setiastro/saspro/sfcc.py +1430 -0
- setiastro/saspro/shortcuts.py +3043 -0
- setiastro/saspro/signature_insert.py +1099 -0
- setiastro/saspro/stacking_suite.py +18181 -0
- setiastro/saspro/star_alignment.py +7420 -0
- setiastro/saspro/star_alignment_preset.py +329 -0
- setiastro/saspro/star_metrics.py +49 -0
- setiastro/saspro/star_spikes.py +681 -0
- setiastro/saspro/star_stretch.py +470 -0
- setiastro/saspro/stat_stretch.py +506 -0
- setiastro/saspro/status_log_dock.py +78 -0
- setiastro/saspro/subwindow.py +3267 -0
- setiastro/saspro/supernovaasteroidhunter.py +1716 -0
- setiastro/saspro/swap_manager.py +99 -0
- setiastro/saspro/torch_backend.py +89 -0
- setiastro/saspro/torch_rejection.py +434 -0
- setiastro/saspro/translations/de_translations.py +3733 -0
- setiastro/saspro/translations/es_translations.py +3923 -0
- setiastro/saspro/translations/fr_translations.py +3842 -0
- setiastro/saspro/translations/integrate_translations.py +234 -0
- setiastro/saspro/translations/it_translations.py +3662 -0
- setiastro/saspro/translations/ja_translations.py +3585 -0
- setiastro/saspro/translations/pt_translations.py +3853 -0
- setiastro/saspro/translations/saspro_de.qm +0 -0
- setiastro/saspro/translations/saspro_de.ts +253 -0
- setiastro/saspro/translations/saspro_es.qm +0 -0
- setiastro/saspro/translations/saspro_es.ts +12520 -0
- setiastro/saspro/translations/saspro_fr.qm +0 -0
- setiastro/saspro/translations/saspro_fr.ts +12514 -0
- setiastro/saspro/translations/saspro_it.qm +0 -0
- setiastro/saspro/translations/saspro_it.ts +12520 -0
- setiastro/saspro/translations/saspro_ja.qm +0 -0
- setiastro/saspro/translations/saspro_ja.ts +257 -0
- setiastro/saspro/translations/saspro_pt.qm +0 -0
- setiastro/saspro/translations/saspro_pt.ts +257 -0
- setiastro/saspro/translations/saspro_zh.qm +0 -0
- setiastro/saspro/translations/saspro_zh.ts +12520 -0
- setiastro/saspro/translations/zh_translations.py +3659 -0
- setiastro/saspro/versioning.py +71 -0
- setiastro/saspro/view_bundle.py +1555 -0
- setiastro/saspro/wavescale_hdr.py +624 -0
- setiastro/saspro/wavescale_hdr_preset.py +101 -0
- setiastro/saspro/wavescalede.py +658 -0
- setiastro/saspro/wavescalede_preset.py +230 -0
- setiastro/saspro/wcs_update.py +374 -0
- setiastro/saspro/whitebalance.py +456 -0
- setiastro/saspro/widgets/__init__.py +48 -0
- setiastro/saspro/widgets/common_utilities.py +306 -0
- setiastro/saspro/widgets/graphics_views.py +122 -0
- setiastro/saspro/widgets/image_utils.py +518 -0
- setiastro/saspro/widgets/preview_dialogs.py +280 -0
- setiastro/saspro/widgets/spinboxes.py +275 -0
- setiastro/saspro/widgets/themed_buttons.py +13 -0
- setiastro/saspro/widgets/wavelet_utils.py +299 -0
- setiastro/saspro/window_shelf.py +185 -0
- setiastro/saspro/xisf.py +1123 -0
- setiastrosuitepro-1.6.1.dist-info/METADATA +267 -0
- setiastrosuitepro-1.6.1.dist-info/RECORD +342 -0
- setiastrosuitepro-1.6.1.dist-info/WHEEL +4 -0
- setiastrosuitepro-1.6.1.dist-info/entry_points.txt +6 -0
- setiastrosuitepro-1.6.1.dist-info/licenses/LICENSE +674 -0
- setiastrosuitepro-1.6.1.dist-info/licenses/license.txt +2580 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# pro/logging_config.py
|
|
2
|
+
"""
|
|
3
|
+
Centralized logging configuration for Seti Astro Suite Pro.
|
|
4
|
+
|
|
5
|
+
Provides:
|
|
6
|
+
- Structured logging with consistent formatting
|
|
7
|
+
- Performance logging decorators
|
|
8
|
+
- Context managers for timed operations
|
|
9
|
+
- Log rotation and file output
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import functools
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
20
|
+
from contextlib import contextmanager
|
|
21
|
+
|
|
22
|
+
# For type hints
|
|
23
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ============================================================================
|
|
27
|
+
# Custom Formatters
|
|
28
|
+
# ============================================================================
|
|
29
|
+
|
|
30
|
+
class ColoredFormatter(logging.Formatter):
|
|
31
|
+
"""
|
|
32
|
+
Colored formatter for console output.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
COLORS = {
|
|
36
|
+
logging.DEBUG: '\033[36m', # Cyan
|
|
37
|
+
logging.INFO: '\033[32m', # Green
|
|
38
|
+
logging.WARNING: '\033[33m', # Yellow
|
|
39
|
+
logging.ERROR: '\033[31m', # Red
|
|
40
|
+
logging.CRITICAL: '\033[35m', # Magenta
|
|
41
|
+
}
|
|
42
|
+
RESET = '\033[0m'
|
|
43
|
+
|
|
44
|
+
def format(self, record):
|
|
45
|
+
color = self.COLORS.get(record.levelno, self.RESET)
|
|
46
|
+
record.levelname = f"{color}{record.levelname}{self.RESET}"
|
|
47
|
+
return super().format(record)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class StructuredFormatter(logging.Formatter):
|
|
51
|
+
"""
|
|
52
|
+
Structured formatter for file output.
|
|
53
|
+
Includes timestamp, level, module, function, and message.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def format(self, record):
|
|
57
|
+
# Add extra context if available
|
|
58
|
+
extra = ""
|
|
59
|
+
if hasattr(record, 'duration_ms'):
|
|
60
|
+
extra += f" [duration={record.duration_ms:.2f}ms]"
|
|
61
|
+
if hasattr(record, 'memory_mb'):
|
|
62
|
+
extra += f" [memory={record.memory_mb:.1f}MB]"
|
|
63
|
+
if hasattr(record, 'context'):
|
|
64
|
+
extra += f" [context={record.context}]"
|
|
65
|
+
|
|
66
|
+
record.extra = extra
|
|
67
|
+
return super().format(record)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ============================================================================
|
|
71
|
+
# Logger Setup
|
|
72
|
+
# ============================================================================
|
|
73
|
+
|
|
74
|
+
def setup_logging(
|
|
75
|
+
app_name: str = "SetiAstroSuite",
|
|
76
|
+
log_dir: Optional[Path] = None,
|
|
77
|
+
console_level: int = logging.INFO,
|
|
78
|
+
file_level: int = logging.DEBUG,
|
|
79
|
+
max_file_size_mb: int = 10,
|
|
80
|
+
backup_count: int = 5,
|
|
81
|
+
enable_colors: bool = True
|
|
82
|
+
) -> logging.Logger:
|
|
83
|
+
"""
|
|
84
|
+
Setup centralized logging configuration.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
app_name: Application name for the logger
|
|
88
|
+
log_dir: Directory for log files (default: ~/.setiastrosuite/logs)
|
|
89
|
+
console_level: Logging level for console output
|
|
90
|
+
file_level: Logging level for file output
|
|
91
|
+
max_file_size_mb: Maximum size of each log file
|
|
92
|
+
backup_count: Number of backup files to keep
|
|
93
|
+
enable_colors: Whether to use colored console output
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Configured logger
|
|
97
|
+
"""
|
|
98
|
+
# Create or get the main logger
|
|
99
|
+
logger = logging.getLogger(app_name)
|
|
100
|
+
|
|
101
|
+
# Avoid adding handlers multiple times
|
|
102
|
+
if logger.handlers:
|
|
103
|
+
return logger
|
|
104
|
+
|
|
105
|
+
logger.setLevel(logging.DEBUG) # Capture all, filter at handlers
|
|
106
|
+
|
|
107
|
+
# Console handler
|
|
108
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
109
|
+
console_handler.setLevel(console_level)
|
|
110
|
+
|
|
111
|
+
if enable_colors and sys.stdout.isatty():
|
|
112
|
+
console_format = ColoredFormatter(
|
|
113
|
+
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
|
114
|
+
datefmt='%H:%M:%S'
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
console_format = logging.Formatter(
|
|
118
|
+
'%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
|
119
|
+
datefmt='%H:%M:%S'
|
|
120
|
+
)
|
|
121
|
+
console_handler.setFormatter(console_format)
|
|
122
|
+
logger.addHandler(console_handler)
|
|
123
|
+
|
|
124
|
+
# File handler (optional)
|
|
125
|
+
if log_dir is not None:
|
|
126
|
+
try:
|
|
127
|
+
log_dir = Path(log_dir)
|
|
128
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
129
|
+
|
|
130
|
+
# Use RotatingFileHandler if available
|
|
131
|
+
try:
|
|
132
|
+
from logging.handlers import RotatingFileHandler
|
|
133
|
+
|
|
134
|
+
log_file = log_dir / f"{app_name.lower()}.log"
|
|
135
|
+
file_handler = RotatingFileHandler(
|
|
136
|
+
log_file,
|
|
137
|
+
maxBytes=max_file_size_mb * 1024 * 1024,
|
|
138
|
+
backupCount=backup_count,
|
|
139
|
+
encoding='utf-8'
|
|
140
|
+
)
|
|
141
|
+
except ImportError:
|
|
142
|
+
# Fallback to regular FileHandler
|
|
143
|
+
log_file = log_dir / f"{app_name.lower()}_{datetime.now():%Y%m%d}.log"
|
|
144
|
+
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
145
|
+
|
|
146
|
+
file_handler.setLevel(file_level)
|
|
147
|
+
file_format = StructuredFormatter(
|
|
148
|
+
'%(asctime)s [%(levelname)s] %(name)s.%(funcName)s:%(lineno)d - %(message)s%(extra)s',
|
|
149
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
150
|
+
)
|
|
151
|
+
file_handler.setFormatter(file_format)
|
|
152
|
+
logger.addHandler(file_handler)
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.warning(f"Could not setup file logging: {e}")
|
|
156
|
+
|
|
157
|
+
return logger
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_logger(name: str = "SetiAstroSuite") -> logging.Logger:
|
|
161
|
+
"""
|
|
162
|
+
Get a logger with the given name.
|
|
163
|
+
|
|
164
|
+
If the logger doesn't exist, it will be created as a child of the main logger.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
name: Logger name (e.g., "SetiAstroSuite.stacking")
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Logger instance
|
|
171
|
+
"""
|
|
172
|
+
return logging.getLogger(name)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ============================================================================
|
|
176
|
+
# Performance Logging Decorators
|
|
177
|
+
# ============================================================================
|
|
178
|
+
|
|
179
|
+
def log_performance(
|
|
180
|
+
logger: Optional[logging.Logger] = None,
|
|
181
|
+
level: int = logging.DEBUG,
|
|
182
|
+
log_args: bool = False,
|
|
183
|
+
log_result: bool = False
|
|
184
|
+
) -> Callable[[F], F]:
|
|
185
|
+
"""
|
|
186
|
+
Decorator to log function execution time.
|
|
187
|
+
|
|
188
|
+
Usage:
|
|
189
|
+
@log_performance()
|
|
190
|
+
def my_function(x, y):
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
@log_performance(logger=my_logger, level=logging.INFO)
|
|
194
|
+
def important_function():
|
|
195
|
+
...
|
|
196
|
+
"""
|
|
197
|
+
def decorator(func: F) -> F:
|
|
198
|
+
@functools.wraps(func)
|
|
199
|
+
def wrapper(*args, **kwargs):
|
|
200
|
+
_logger = logger or get_logger()
|
|
201
|
+
|
|
202
|
+
# Log start
|
|
203
|
+
msg_parts = [f"Starting {func.__qualname__}"]
|
|
204
|
+
if log_args:
|
|
205
|
+
msg_parts.append(f"args={args}, kwargs={kwargs}")
|
|
206
|
+
_logger.log(level, " ".join(msg_parts))
|
|
207
|
+
|
|
208
|
+
# Execute and time
|
|
209
|
+
start_time = time.perf_counter()
|
|
210
|
+
try:
|
|
211
|
+
result = func(*args, **kwargs)
|
|
212
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
213
|
+
|
|
214
|
+
# Log completion
|
|
215
|
+
msg = f"Completed {func.__qualname__} in {duration_ms:.2f}ms"
|
|
216
|
+
if log_result:
|
|
217
|
+
msg += f" result={result}"
|
|
218
|
+
_logger.log(level, msg)
|
|
219
|
+
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
224
|
+
_logger.error(
|
|
225
|
+
f"Failed {func.__qualname__} after {duration_ms:.2f}ms: {type(e).__name__}: {e}"
|
|
226
|
+
)
|
|
227
|
+
raise
|
|
228
|
+
|
|
229
|
+
return wrapper # type: ignore
|
|
230
|
+
|
|
231
|
+
return decorator
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def log_slow_operations(
|
|
235
|
+
threshold_ms: float = 1000,
|
|
236
|
+
logger: Optional[logging.Logger] = None
|
|
237
|
+
) -> Callable[[F], F]:
|
|
238
|
+
"""
|
|
239
|
+
Decorator to log operations that exceed a time threshold.
|
|
240
|
+
|
|
241
|
+
Usage:
|
|
242
|
+
@log_slow_operations(threshold_ms=500)
|
|
243
|
+
def possibly_slow_function():
|
|
244
|
+
...
|
|
245
|
+
"""
|
|
246
|
+
def decorator(func: F) -> F:
|
|
247
|
+
@functools.wraps(func)
|
|
248
|
+
def wrapper(*args, **kwargs):
|
|
249
|
+
start_time = time.perf_counter()
|
|
250
|
+
result = func(*args, **kwargs)
|
|
251
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
252
|
+
|
|
253
|
+
if duration_ms >= threshold_ms:
|
|
254
|
+
_logger = logger or get_logger()
|
|
255
|
+
_logger.warning(
|
|
256
|
+
f"Slow operation: {func.__qualname__} took {duration_ms:.2f}ms "
|
|
257
|
+
f"(threshold: {threshold_ms}ms)"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
return result
|
|
261
|
+
|
|
262
|
+
return wrapper # type: ignore
|
|
263
|
+
|
|
264
|
+
return decorator
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ============================================================================
|
|
268
|
+
# Context Managers
|
|
269
|
+
# ============================================================================
|
|
270
|
+
|
|
271
|
+
@contextmanager
|
|
272
|
+
def log_timing(
|
|
273
|
+
operation_name: str,
|
|
274
|
+
logger: Optional[logging.Logger] = None,
|
|
275
|
+
level: int = logging.DEBUG
|
|
276
|
+
):
|
|
277
|
+
"""
|
|
278
|
+
Context manager for logging operation timing.
|
|
279
|
+
|
|
280
|
+
Usage:
|
|
281
|
+
with log_timing("Image processing"):
|
|
282
|
+
process_image(data)
|
|
283
|
+
"""
|
|
284
|
+
_logger = logger or get_logger()
|
|
285
|
+
_logger.log(level, f"Starting: {operation_name}")
|
|
286
|
+
|
|
287
|
+
start_time = time.perf_counter()
|
|
288
|
+
try:
|
|
289
|
+
yield
|
|
290
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
291
|
+
_logger.log(level, f"Completed: {operation_name} in {duration_ms:.2f}ms")
|
|
292
|
+
except Exception as e:
|
|
293
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
294
|
+
_logger.error(f"Failed: {operation_name} after {duration_ms:.2f}ms: {e}")
|
|
295
|
+
raise
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@contextmanager
|
|
299
|
+
def log_memory(
|
|
300
|
+
operation_name: str,
|
|
301
|
+
logger: Optional[logging.Logger] = None,
|
|
302
|
+
level: int = logging.DEBUG
|
|
303
|
+
):
|
|
304
|
+
"""
|
|
305
|
+
Context manager for logging memory usage changes.
|
|
306
|
+
|
|
307
|
+
Usage:
|
|
308
|
+
with log_memory("Loading large dataset"):
|
|
309
|
+
load_dataset(path)
|
|
310
|
+
"""
|
|
311
|
+
try:
|
|
312
|
+
import psutil
|
|
313
|
+
process = psutil.Process()
|
|
314
|
+
has_psutil = True
|
|
315
|
+
except ImportError:
|
|
316
|
+
has_psutil = False
|
|
317
|
+
|
|
318
|
+
_logger = logger or get_logger()
|
|
319
|
+
|
|
320
|
+
if not has_psutil:
|
|
321
|
+
yield
|
|
322
|
+
return
|
|
323
|
+
|
|
324
|
+
mem_before = process.memory_info().rss / (1024 * 1024)
|
|
325
|
+
_logger.log(level, f"Starting: {operation_name} (memory: {mem_before:.1f}MB)")
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
yield
|
|
329
|
+
mem_after = process.memory_info().rss / (1024 * 1024)
|
|
330
|
+
delta = mem_after - mem_before
|
|
331
|
+
sign = "+" if delta >= 0 else ""
|
|
332
|
+
_logger.log(
|
|
333
|
+
level,
|
|
334
|
+
f"Completed: {operation_name} (memory: {mem_after:.1f}MB, {sign}{delta:.1f}MB)"
|
|
335
|
+
)
|
|
336
|
+
except Exception as e:
|
|
337
|
+
mem_after = process.memory_info().rss / (1024 * 1024)
|
|
338
|
+
_logger.error(f"Failed: {operation_name} (memory: {mem_after:.1f}MB): {e}")
|
|
339
|
+
raise
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# ============================================================================
|
|
343
|
+
# Progress Logger
|
|
344
|
+
# ============================================================================
|
|
345
|
+
|
|
346
|
+
class ProgressLogger:
|
|
347
|
+
"""
|
|
348
|
+
Logger for tracking progress of long operations.
|
|
349
|
+
|
|
350
|
+
Usage:
|
|
351
|
+
with ProgressLogger("Processing files", total=100) as progress:
|
|
352
|
+
for i, file in enumerate(files):
|
|
353
|
+
process(file)
|
|
354
|
+
progress.update(i + 1)
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
def __init__(
|
|
358
|
+
self,
|
|
359
|
+
operation_name: str,
|
|
360
|
+
total: int,
|
|
361
|
+
logger: Optional[logging.Logger] = None,
|
|
362
|
+
log_interval: int = 10, # Log every N percent
|
|
363
|
+
level: int = logging.INFO
|
|
364
|
+
):
|
|
365
|
+
self.operation_name = operation_name
|
|
366
|
+
self.total = total
|
|
367
|
+
self.logger = logger or get_logger()
|
|
368
|
+
self.log_interval = log_interval
|
|
369
|
+
self.level = level
|
|
370
|
+
self.current = 0
|
|
371
|
+
self.last_logged_percent = -1
|
|
372
|
+
self.start_time: Optional[float] = None
|
|
373
|
+
|
|
374
|
+
def __enter__(self):
|
|
375
|
+
self.start_time = time.perf_counter()
|
|
376
|
+
self.logger.log(self.level, f"Starting: {self.operation_name} (0/{self.total})")
|
|
377
|
+
return self
|
|
378
|
+
|
|
379
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
380
|
+
duration = time.perf_counter() - self.start_time if self.start_time else 0
|
|
381
|
+
|
|
382
|
+
if exc_type is None:
|
|
383
|
+
self.logger.log(
|
|
384
|
+
self.level,
|
|
385
|
+
f"Completed: {self.operation_name} ({self.total}/{self.total}) "
|
|
386
|
+
f"in {duration:.2f}s"
|
|
387
|
+
)
|
|
388
|
+
else:
|
|
389
|
+
self.logger.error(
|
|
390
|
+
f"Failed: {self.operation_name} at {self.current}/{self.total} "
|
|
391
|
+
f"after {duration:.2f}s: {exc_val}"
|
|
392
|
+
)
|
|
393
|
+
return False
|
|
394
|
+
|
|
395
|
+
def update(self, current: int, message: str = ""):
|
|
396
|
+
"""Update progress and log if interval reached."""
|
|
397
|
+
self.current = current
|
|
398
|
+
|
|
399
|
+
if self.total <= 0:
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
percent = int((current / self.total) * 100)
|
|
403
|
+
|
|
404
|
+
# Log at intervals
|
|
405
|
+
if percent >= self.last_logged_percent + self.log_interval:
|
|
406
|
+
self.last_logged_percent = (percent // self.log_interval) * self.log_interval
|
|
407
|
+
|
|
408
|
+
elapsed = time.perf_counter() - self.start_time if self.start_time else 0
|
|
409
|
+
eta = (elapsed / current * (self.total - current)) if current > 0 else 0
|
|
410
|
+
|
|
411
|
+
msg = f"Progress: {self.operation_name} {percent}% ({current}/{self.total})"
|
|
412
|
+
if message:
|
|
413
|
+
msg += f" - {message}"
|
|
414
|
+
msg += f" [elapsed: {elapsed:.1f}s, ETA: {eta:.1f}s]"
|
|
415
|
+
|
|
416
|
+
self.logger.log(self.level, msg)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
# ============================================================================
|
|
420
|
+
# Convenience Exports
|
|
421
|
+
# ============================================================================
|
|
422
|
+
|
|
423
|
+
# Pre-configured logger for common use
|
|
424
|
+
_main_logger: Optional[logging.Logger] = None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def init_app_logging(log_dir: Optional[Path] = None) -> logging.Logger:
|
|
428
|
+
"""
|
|
429
|
+
Initialize application logging.
|
|
430
|
+
|
|
431
|
+
Call this once at application startup.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
log_dir: Directory for log files (optional)
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Main application logger
|
|
438
|
+
"""
|
|
439
|
+
global _main_logger
|
|
440
|
+
|
|
441
|
+
if log_dir is None:
|
|
442
|
+
# Default to user's home directory
|
|
443
|
+
log_dir = Path.home() / ".setiastrosuite" / "logs"
|
|
444
|
+
|
|
445
|
+
_main_logger = setup_logging(
|
|
446
|
+
app_name="SetiAstroSuite",
|
|
447
|
+
log_dir=log_dir,
|
|
448
|
+
console_level=logging.INFO,
|
|
449
|
+
file_level=logging.DEBUG
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
return _main_logger
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def app_logger() -> logging.Logger:
|
|
456
|
+
"""Get the main application logger."""
|
|
457
|
+
global _main_logger
|
|
458
|
+
if _main_logger is None:
|
|
459
|
+
_main_logger = setup_logging(app_name="SetiAstroSuite")
|
|
460
|
+
return _main_logger
|