import streamlit as st
import pandas as pd
import plotly.graph_objects as go
import pickle
from statsmodels.tsa.arima.model import ARIMA
import os
from datetime import datetime, timedelta
# Page configuration with custom theme
st.set_page_config(
page_title="ERROR Log Analytics Dashboard",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS to enhance the UI
st.markdown("""
""", unsafe_allow_html=True)
# Sidebar for controls and information
with st.sidebar:
st.image("https://www.svgrepo.com/show/374111/log.svg", width=100)
st.markdown("## ERROR Log Analytics")
st.markdown("---")
st.markdown("### Model Configuration")
# File selection (keeping default path but making it look configurable)
file_path = st.text_input("Log file path", value="augmented_logs.csv")
# ARIMA parameters
st.markdown("### ARIMA Parameters")
p = st.slider("Auto-regression (p)", min_value=0, max_value=5, value=1)
d = st.slider("Differencing (d)", min_value=0, max_value=2, value=1)
q = st.slider("Moving Average (q)", min_value=0, max_value=5, value=1)
# Forecast range
st.markdown("### Forecast Settings")
forecast_days = st.slider("Forecast Horizon (days)", min_value=1, max_value=30, value=7)
st.markdown("---")
st.markdown("*This dashboard analyzes ERROR logs and forecasts future error rates using ARIMA modeling.*")
# Main content
st.markdown('
📊 ERROR Log Analysis & Forecasting
', unsafe_allow_html=True)
if not os.path.exists(file_path):
st.error(f"❌ File not found: {file_path}")
else:
try:
# Main app container
with st.container():
# Load and prep data
df = pd.read_csv(file_path)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['date'] = df['timestamp'].dt.date
# Group by date and count ERRORs
daily_errors = df[df['log_level'] == 'ERROR'].groupby('date').size()
daily_errors_ts = daily_errors.asfreq('D').fillna(0)
# Display key metrics
st.markdown('', unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
with col1:
st.markdown('', unsafe_allow_html=True)
st.metric("Total ERRORs", f"{int(daily_errors_ts.sum())}")
st.markdown('
', unsafe_allow_html=True)
with col2:
st.markdown('', unsafe_allow_html=True)
st.metric("Average Daily ERRORs", f"{daily_errors_ts.mean():.2f}")
st.markdown('
', unsafe_allow_html=True)
with col3:
st.markdown('', unsafe_allow_html=True)
if len(daily_errors_ts) >= 7:
last_week = daily_errors_ts[-7:].mean()
previous_week = daily_errors_ts[-14:-7].mean()
delta = ((last_week - previous_week) / previous_week * 100) if previous_week > 0 else 0
st.metric("7-Day Trend", f"{last_week:.2f}", f"{delta:.1f}%")
else:
st.metric("7-Day Trend", "Insufficient data")
st.markdown('
', unsafe_allow_html=True)
# Historical data visualization
st.markdown('', unsafe_allow_html=True)
st.markdown('', unsafe_allow_html=True)
chart_tab1, chart_tab2 = st.tabs(["Line Chart", "Bar Chart"])
with chart_tab1:
st.line_chart(daily_errors_ts)
with chart_tab2:
st.bar_chart(daily_errors_ts)
st.markdown('
', unsafe_allow_html=True)
# Model training
st.markdown('', unsafe_allow_html=True)
st.markdown('', unsafe_allow_html=True)
try:
with st.spinner("Training ARIMA model..."):
# Use the parameters from sidebar
model = ARIMA(daily_errors_ts, order=(p, d, q))
model_fit = model.fit()
# Save model
with open("arima_model.pkl", "wb") as f:
pickle.dump(model_fit, f)
st.markdown('
✅ ARIMA model trained and saved successfully!
',
unsafe_allow_html=True)
# Display model summary in expander
with st.expander("View Model Details"):
st.code(str(model_fit.summary()))
except Exception as arima_error:
st.error(f"⚠️ ARIMA training failed: {arima_error}")
st.markdown('
', unsafe_allow_html=True)
# Forecast visualization
st.markdown('', unsafe_allow_html=True)
st.markdown('', unsafe_allow_html=True)
forecast = model_fit.forecast(steps=forecast_days)
forecast_dates = pd.date_range(start=daily_errors_ts.index[-1] + pd.Timedelta(days=1),
periods=forecast_days)
# Create forecast dataframe for additional analysis
forecast_df = pd.DataFrame({
'date': forecast_dates,
'forecast': forecast.values,
# Use a fixed standard deviation estimate instead of se_mean
'lower_ci': forecast.values - 2 * forecast.values.std() if len(forecast) > 1 else forecast.values,
'upper_ci': forecast.values + 2 * forecast.values.std() if len(forecast) > 1 else forecast.values * 1.2
})
# Round negative values to 0 for logical consistency
forecast_df['lower_ci'] = forecast_df['lower_ci'].clip(lower=0)
forecast_df['forecast'] = forecast_df['forecast'].clip(lower=0)
# Enhanced plotly visualization
fig = go.Figure()
# Historical data
fig.add_trace(go.Scatter(
x=daily_errors_ts.index,
y=daily_errors_ts.values,
mode='lines+markers',
name='Historical ERRORs',
line=dict(color='#3B82F6', width=2)
))
# Forecast
fig.add_trace(go.Scatter(
x=forecast_df['date'],
y=forecast_df['forecast'],
mode='lines+markers',
name='Forecasted ERRORs',
line=dict(color='#EF4444', width=2, dash='dash')
))
# Confidence interval
fig.add_trace(go.Scatter(
x=forecast_df['date'].tolist() + forecast_df['date'].tolist()[::-1],
y=forecast_df['upper_ci'].tolist() + forecast_df['lower_ci'].tolist()[::-1],
fill='toself',
fillcolor='rgba(239, 68, 68, 0.1)',
line=dict(color='rgba(0,0,0,0)'),
hoverinfo='skip',
showlegend=False
))
fig.update_layout(
title='ERROR Log Forecast with Confidence Intervals',
xaxis_title='Date',
yaxis_title='ERROR Count',
hovermode='x unified',
legend=dict(x=0.01, y=0.99),
template='plotly_white',
height=500
)
st.plotly_chart(fig, use_container_width=True)
# Forecast data table
with st.expander("View Forecast Data"):
forecast_df['date'] = forecast_df['date'].dt.date
forecast_df['forecast'] = forecast_df['forecast'].round(2)
forecast_df['lower_ci'] = forecast_df['lower_ci'].round(2)
forecast_df['upper_ci'] = forecast_df['upper_ci'].round(2)
st.dataframe(forecast_df)
# Download forecast as CSV
csv = forecast_df.to_csv(index=False)
st.download_button(
label="Download Forecast CSV",
data=csv,
file_name=f"error_forecast_{datetime.now().strftime('%Y%m%d')}.csv",
mime="text/csv"
)
st.markdown('
', unsafe_allow_html=True)
except Exception as e:
st.error(f"❌ Error processing data: {e}")