| 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
|
|
|
|
|
| st.set_page_config(
|
| page_title="ERROR Log Analytics Dashboard",
|
| layout="wide",
|
| initial_sidebar_state="expanded"
|
| )
|
|
|
|
|
| st.markdown("""
|
| <style>
|
| .main-header {
|
| font-size: 2.5rem;
|
| font-weight: 700;
|
| color: #1E3A8A;
|
| margin-bottom: 1rem;
|
| }
|
| .sub-header {
|
| font-size: 1.5rem;
|
| font-weight: 600;
|
| color: #2563EB;
|
| margin-top: 2rem;
|
| }
|
| .card {
|
| background-color: #F8FAFC;
|
| border-radius: 0.5rem;
|
| padding: 1.5rem;
|
| box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
| margin-bottom: 1rem;
|
| }
|
| .success-msg {
|
| background-color: #DCFCE7;
|
| color: #166534;
|
| padding: 0.75rem;
|
| border-radius: 0.375rem;
|
| border-left: 4px solid #16A34A;
|
| margin: 1rem 0;
|
| }
|
| .stButton>button {
|
| background-color: #2563EB;
|
| color: white;
|
| border: none;
|
| border-radius: 0.375rem;
|
| padding: 0.5rem 1rem;
|
| }
|
| .metrics-container {
|
| display: flex;
|
| justify-content: space-between;
|
| }
|
| .metric-card {
|
| background-color: #F0F9FF;
|
| border-radius: 0.5rem;
|
| padding: 1rem;
|
| text-align: center;
|
| box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
| width: 32%;
|
| }
|
| </style>
|
| """, unsafe_allow_html=True)
|
|
|
|
|
| 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_path = st.text_input("Log file path", value="augmented_logs.csv")
|
|
|
|
|
| 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)
|
|
|
|
|
| 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.*")
|
|
|
|
|
| st.markdown('<div class="main-header">📊 ERROR Log Analysis & Forecasting</div>', unsafe_allow_html=True)
|
|
|
| if not os.path.exists(file_path):
|
| st.error(f"❌ File not found: {file_path}")
|
| else:
|
| try:
|
|
|
| with st.container():
|
|
|
| df = pd.read_csv(file_path)
|
| df['timestamp'] = pd.to_datetime(df['timestamp'])
|
| df['date'] = df['timestamp'].dt.date
|
|
|
|
|
| daily_errors = df[df['log_level'] == 'ERROR'].groupby('date').size()
|
| daily_errors_ts = daily_errors.asfreq('D').fillna(0)
|
|
|
|
|
| st.markdown('<div class="sub-header">Key Metrics</div>', unsafe_allow_html=True)
|
|
|
| col1, col2, col3 = st.columns(3)
|
| with col1:
|
| st.markdown('<div class="card">', unsafe_allow_html=True)
|
| st.metric("Total ERRORs", f"{int(daily_errors_ts.sum())}")
|
| st.markdown('</div>', unsafe_allow_html=True)
|
|
|
| with col2:
|
| st.markdown('<div class="card">', unsafe_allow_html=True)
|
| st.metric("Average Daily ERRORs", f"{daily_errors_ts.mean():.2f}")
|
| st.markdown('</div>', unsafe_allow_html=True)
|
|
|
| with col3:
|
| st.markdown('<div class="card">', 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('</div>', unsafe_allow_html=True)
|
|
|
|
|
| st.markdown('<div class="sub-header">Historical ERROR Trends</div>', unsafe_allow_html=True)
|
| st.markdown('<div class="card">', 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('</div>', unsafe_allow_html=True)
|
|
|
|
|
| st.markdown('<div class="sub-header">ARIMA Model Training</div>', unsafe_allow_html=True)
|
| st.markdown('<div class="card">', unsafe_allow_html=True)
|
|
|
| try:
|
| with st.spinner("Training ARIMA model..."):
|
|
|
| model = ARIMA(daily_errors_ts, order=(p, d, q))
|
| model_fit = model.fit()
|
|
|
|
|
| with open("arima_model.pkl", "wb") as f:
|
| pickle.dump(model_fit, f)
|
|
|
| st.markdown('<div class="success-msg">✅ ARIMA model trained and saved successfully!</div>',
|
| unsafe_allow_html=True)
|
|
|
|
|
| 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('</div>', unsafe_allow_html=True)
|
|
|
|
|
| st.markdown('<div class="sub-header">ERROR Forecast Analysis</div>', unsafe_allow_html=True)
|
| st.markdown('<div class="card">', 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)
|
|
|
|
|
| forecast_df = pd.DataFrame({
|
| 'date': forecast_dates,
|
| 'forecast': forecast.values,
|
|
|
| '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
|
| })
|
|
|
|
|
| forecast_df['lower_ci'] = forecast_df['lower_ci'].clip(lower=0)
|
| forecast_df['forecast'] = forecast_df['forecast'].clip(lower=0)
|
|
|
|
|
| fig = go.Figure()
|
|
|
|
|
| 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)
|
| ))
|
|
|
|
|
| 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')
|
| ))
|
|
|
|
|
| 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)
|
|
|
|
|
| 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)
|
|
|
|
|
| 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('</div>', unsafe_allow_html=True)
|
|
|
| except Exception as e:
|
| st.error(f"❌ Error processing data: {e}") |