Skip to content

Commit ffb30dc

Browse files
committed
Relocate libopenshot.log recovery to exceptions.py
1 parent cd81a5f commit ffb30dc

File tree

2 files changed

+110
-108
lines changed

2 files changed

+110
-108
lines changed

src/classes/exceptions.py

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,117 @@
2525
along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
2626
"""
2727

28+
import os
2829
import traceback
30+
import platform
31+
32+
from classes import info
2933
from classes.logger import log
30-
from classes.metrics import track_exception_stacktrace
34+
from classes.metrics import track_exception_stacktrace, track_metric_error
3135

3236

3337
def ExceptionHandler(exeception_type, exeception_value, exeception_traceback):
3438
"""Callback for any unhandled exceptions"""
35-
log.error('Unhandled Exception', exc_info=(exeception_type, exeception_value, exeception_traceback))
39+
log.error(
40+
'Unhandled Exception',
41+
exc_info=(exeception_type, exeception_value, exeception_traceback))
3642

3743
# Build string of stack trace
38-
stacktrace = "Python %s" % "".join(traceback.format_exception(exeception_type, exeception_value, exeception_traceback))
44+
stacktrace = "Python %s" % "".join(
45+
traceback.format_exception(
46+
exeception_type, exeception_value, exeception_traceback))
3947

4048
# Report traceback to webservice (if enabled)
4149
track_exception_stacktrace(stacktrace, "openshot-qt")
50+
51+
52+
def tail_file(f, n, offset=None):
53+
"""Read the end of a file (n number of lines)"""
54+
avg_line_length = 90
55+
to_read = n + (offset or 0)
56+
57+
while True:
58+
try:
59+
# Seek to byte position
60+
f.seek(-(avg_line_length * to_read), 2)
61+
except IOError:
62+
# Byte position not found
63+
f.seek(0)
64+
pos = f.tell()
65+
lines = f.read().splitlines()
66+
if len(lines) >= to_read or pos == 0:
67+
# Return the lines
68+
return lines[-to_read:offset and -offset or None]
69+
avg_line_length *= 2
70+
71+
72+
def libopenshot_crash_recovery():
73+
"""Walk libopenshot.log for the last line before this launch"""
74+
log_path = os.path.join(info.USER_PATH, "libopenshot.log")
75+
last_log_line = ""
76+
last_stack_trace = ""
77+
found_stack = False
78+
log_start_counter = 0
79+
if not os.path.exists(log_path):
80+
return
81+
with open(log_path, "rb") as f:
82+
# Read from bottom up
83+
for raw_line in reversed(tail_file(f, 500)):
84+
line = str(raw_line, 'utf-8')
85+
# Detect stack trace
86+
if "End of Stack Trace" in line:
87+
found_stack = True
88+
continue
89+
if "Unhandled Exception: Stack Trace" in line:
90+
found_stack = False
91+
continue
92+
if "libopenshot logging:" in line:
93+
log_start_counter += 1
94+
if log_start_counter > 1:
95+
# Found the previous log start, too old now
96+
break
97+
98+
if found_stack:
99+
# Append line to beginning of stacktrace
100+
last_stack_trace = line + last_stack_trace
101+
102+
# Ignore certain useless lines
103+
line.strip()
104+
if all(["---" not in line,
105+
"libopenshot logging:" not in line,
106+
not last_log_line,
107+
]):
108+
last_log_line = line
109+
110+
# Split last stack trace (if any)
111+
if last_stack_trace:
112+
# Get top line of stack trace (for metrics)
113+
last_log_line = last_stack_trace.split("\n")[0].strip()
114+
115+
# Send stacktrace for debugging (if send metrics is enabled)
116+
track_exception_stacktrace(last_stack_trace, "libopenshot")
117+
118+
# Clear / normalize log line (so we can roll them up in the analytics)
119+
if last_log_line:
120+
# Format last log line based on OS (since each OS can be formatted differently)
121+
if platform.system() == "Darwin":
122+
last_log_line = "mac-%s" % last_log_line[58:].strip()
123+
elif platform.system() == "Windows":
124+
last_log_line = "windows-%s" % last_log_line
125+
elif platform.system() == "Linux":
126+
last_log_line = "linux-%s" % last_log_line.replace("/usr/local/lib/", "")
127+
128+
# Remove '()' from line, and split. Trying to grab the beginning of the log line.
129+
last_log_line = last_log_line.replace("()", "")
130+
log_parts = last_log_line.split("(")
131+
if len(log_parts) == 2:
132+
last_log_line = "-%s" % log_parts[0].replace(
133+
"logger_libopenshot:INFO ", "").strip()[:64]
134+
elif len(log_parts) >= 3:
135+
last_log_line = "-%s (%s" % (log_parts[0].replace(
136+
"logger_libopenshot:INFO ", "").strip()[:64], log_parts[1])
137+
else:
138+
last_log_line = ""
139+
140+
# Report exception (with last libopenshot line... if found)
141+
track_metric_error("unhandled-crash%s" % last_log_line, True)

src/windows/main_window.py

Lines changed: 7 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"""
2929

3030
import os
31-
import platform
3231
import shutil
3332
import sys
3433
import webbrowser
@@ -50,18 +49,15 @@
5049
QLineEdit, QSlider, QLabel, QComboBox, QTextEdit
5150
)
5251

53-
from classes import info, ui_util, settings, qt_types, updates
52+
from classes import exceptions, info, settings, qt_types, ui_util, updates
5453
from classes.app import get_app
5554
from classes.conversion import zoomToSeconds, secondsToZoom
5655
from classes.exporters.edl import export_edl
5756
from classes.exporters.final_cut_pro import export_xml
5857
from classes.importers.edl import import_edl
5958
from classes.importers.final_cut_pro import import_xml
6059
from classes.logger import log
61-
from classes.metrics import (
62-
track_metric_session, track_metric_screen,
63-
track_metric_error, track_exception_stacktrace,
64-
)
60+
from classes.metrics import track_metric_session, track_metric_screen
6561
from classes.query import Clip, Transition, Marker, Track
6662
from classes.thumbnail import httpThumbnailServerThread
6763
from classes.time_parts import secondsToTimecode
@@ -217,101 +213,26 @@ def recover_backup(self):
217213
def create_lock_file(self):
218214
"""Create a lock file"""
219215
lock_path = os.path.join(info.USER_PATH, ".lock")
220-
lock_value = str(uuid4())
221-
222216
# Check if it already exists
223217
if os.path.exists(lock_path):
224-
# Walk the libopenshot log (if found), and try and find last line before this launch
225-
log_path = os.path.join(info.USER_PATH, "libopenshot.log")
226-
last_log_line = ""
227-
last_stack_trace = ""
228-
found_stack = False
229-
log_start_counter = 0
230-
if os.path.exists(log_path):
231-
with open(log_path, "rb") as f:
232-
# Read from bottom up
233-
for raw_line in reversed(self.tail_file(f, 500)):
234-
line = str(raw_line, 'utf-8')
235-
# Detect stack trace
236-
if "End of Stack Trace" in line:
237-
found_stack = True
238-
continue
239-
if "Unhandled Exception: Stack Trace" in line:
240-
found_stack = False
241-
continue
242-
if "libopenshot logging:" in line:
243-
log_start_counter += 1
244-
if log_start_counter > 1:
245-
# Found the previous log start, too old now
246-
break
247-
248-
if found_stack:
249-
# Append line to beginning of stacktrace
250-
last_stack_trace = line + last_stack_trace
251-
252-
# Ignore certain useless lines
253-
line.strip()
254-
if all(["---" not in line,
255-
"libopenshot logging:" not in line,
256-
not last_log_line,
257-
]):
258-
last_log_line = line
259-
260-
# Split last stack trace (if any)
261-
if last_stack_trace:
262-
# Get top line of stack trace (for metrics)
263-
last_log_line = last_stack_trace.split("\n")[0].strip()
264-
265-
# Send stacktrace for debugging (if send metrics is enabled)
266-
track_exception_stacktrace(last_stack_trace, "libopenshot")
267-
268-
# Clear / normalize log line (so we can roll them up in the analytics)
269-
if last_log_line:
270-
# Format last log line based on OS (since each OS can be formatted differently)
271-
if platform.system() == "Darwin":
272-
last_log_line = "mac-%s" % last_log_line[58:].strip()
273-
elif platform.system() == "Windows":
274-
last_log_line = "windows-%s" % last_log_line
275-
elif platform.system() == "Linux":
276-
last_log_line = "linux-%s" % last_log_line.replace("/usr/local/lib/", "")
277-
278-
# Remove '()' from line, and split. Trying to grab the beginning of the log line.
279-
last_log_line = last_log_line.replace("()", "")
280-
log_parts = last_log_line.split("(")
281-
if len(log_parts) == 2:
282-
last_log_line = "-%s" % log_parts[0].replace(
283-
"logger_libopenshot:INFO ", "").strip()[:64]
284-
elif len(log_parts) >= 3:
285-
last_log_line = "-%s (%s" % (log_parts[0].replace(
286-
"logger_libopenshot:INFO ", "").strip()[:64], log_parts[1])
287-
else:
288-
last_log_line = ""
289-
290-
# Throw exception (with last libopenshot line... if found)
291-
log.error(
292-
"Unhandled crash detected... will attempt to recover backup project: %s"
293-
% info.BACKUP_FILE)
294-
track_metric_error("unhandled-crash%s" % last_log_line, True)
295-
296-
# Remove file
218+
exceptions.libopenshot_crash_recovery()
219+
log.error("Unhandled crash detected. Preserving cache.")
297220
self.destroy_lock_file()
298-
299221
else:
300222
# Normal startup, clear thumbnails
301223
self.clear_all_thumbnails()
302224

303225
# Write lock file (try a few times if failure)
226+
lock_value = str(uuid4())
304227
for attempt in range(5):
305228
try:
306229
# Create lock file
307230
with open(lock_path, 'w') as f:
308231
f.write(lock_value)
309-
log.debug("Wrote value {} to lock file {}".format(
310-
lock_value, lock_path))
232+
log.debug("Wrote value %s to lock file %s", lock_value, lock_path)
311233
break
312234
except OSError:
313-
log.debug('Failed to write lock file (attempt: {})'.format(
314-
attempt), exc_info=1)
235+
log.debug("Failed to write lock file (attempt: %d)", attempt, exc_info=1)
315236
sleep(0.25)
316237

317238
def destroy_lock_file(self):
@@ -330,25 +251,6 @@ def destroy_lock_file(self):
330251
log.debug('Failed to destroy lock file (attempt: %s)' % attempt, exc_info=1)
331252
sleep(0.25)
332253

333-
def tail_file(self, f, n, offset=None):
334-
"""Read the end of a file (n number of lines)"""
335-
avg_line_length = 90
336-
to_read = n + (offset or 0)
337-
338-
while True:
339-
try:
340-
# Seek to byte position
341-
f.seek(-(avg_line_length * to_read), 2)
342-
except IOError:
343-
# Byte position not found
344-
f.seek(0)
345-
pos = f.tell()
346-
lines = f.read().splitlines()
347-
if len(lines) >= to_read or pos == 0:
348-
# Return the lines
349-
return lines[-to_read:offset and -offset or None]
350-
avg_line_length *= 2
351-
352254
def actionNew_trigger(self):
353255

354256
app = get_app()

0 commit comments

Comments
 (0)