#2062 Verbose logging option for Broadcastify Feed to troubleshoot streaming connection issues. (#2063)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-10-31 04:11:45 -04:00
committed by GitHub
parent 786ae9369b
commit 40840dbd5c
3 changed files with 156 additions and 38 deletions

View File

@@ -1,23 +1,20 @@
/* /*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* *
* * ****************************************************************************** * This program is free software: you can redistribute it and/or modify
* * Copyright (C) 2014-2020 Dennis Sheirer * it under the terms of the GNU General Public License as published by
* * * the Free Software Foundation, either version 3 of the License, or
* * This program is free software: you can redistribute it and/or modify * (at your option) any later version.
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/ */
package io.github.dsheirer.audio.broadcast.broadcastify; package io.github.dsheirer.audio.broadcast.broadcastify;
@@ -35,6 +32,7 @@ public class BroadcastifyFeedConfiguration extends IcecastTCPConfiguration
private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyFeedConfiguration.class); private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyFeedConfiguration.class);
private int mFeedID; private int mFeedID;
private boolean mVerboseLogging = false;
public BroadcastifyFeedConfiguration() public BroadcastifyFeedConfiguration()
{ {
@@ -123,4 +121,15 @@ public class BroadcastifyFeedConfiguration extends IcecastTCPConfiguration
{ {
mFeedID = feedID; mFeedID = feedID;
} }
@JacksonXmlProperty(isAttribute = true, localName = "verbose_logging")
public boolean isVerboseLogging()
{
return mVerboseLogging;
}
public void setVerboseLogging(boolean verboseLogging)
{
mVerboseLogging = verboseLogging;
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
* ***************************************************************************** * *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer * Copyright (C) 2014-2024 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@ package io.github.dsheirer.audio.broadcast.icecast;
import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.broadcast.BroadcastState; import io.github.dsheirer.audio.broadcast.BroadcastState;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
import io.github.dsheirer.audio.broadcast.icecast.codec.IcecastCodecFactory; import io.github.dsheirer.audio.broadcast.icecast.codec.IcecastCodecFactory;
import io.github.dsheirer.audio.convert.InputAudioFormat; import io.github.dsheirer.audio.convert.InputAudioFormat;
import io.github.dsheirer.audio.convert.MP3AudioConverter; import io.github.dsheirer.audio.convert.MP3AudioConverter;
@@ -39,6 +40,8 @@ import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession; import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector; import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -56,6 +59,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
private IoSession mStreamingSession = null; private IoSession mStreamingSession = null;
private long mLastConnectionAttempt = 0; private long mLastConnectionAttempt = 0;
private boolean mVerboseLogging = false;
private AtomicBoolean mConnecting = new AtomicBoolean(); private AtomicBoolean mConnecting = new AtomicBoolean();
/** /**
@@ -73,6 +77,11 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
MP3Setting mp3Setting, AliasModel aliasModel) MP3Setting mp3Setting, AliasModel aliasModel)
{ {
super(configuration, inputAudioFormat, mp3Setting, aliasModel); super(configuration, inputAudioFormat, mp3Setting, aliasModel);
if(configuration instanceof BroadcastifyFeedConfiguration broadcastify)
{
mVerboseLogging = broadcastify.isVerboseLogging();
}
} }
/** /**
@@ -132,9 +141,12 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
mSocketConnector = new NioSocketConnector(); mSocketConnector = new NioSocketConnector();
mSocketConnector.getSessionConfig().setWriteTimeout(WRITE_TIMEOUT_SECONDS); mSocketConnector.getSessionConfig().setWriteTimeout(WRITE_TIMEOUT_SECONDS);
// LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class); if(mVerboseLogging)
// loggingFilter.setMessageSentLogLevel(LogLevel.NONE); {
// mSocketConnector.getFilterChain().addLast("logger", loggingFilter); LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class);
loggingFilter.setMessageSentLogLevel(LogLevel.NONE);
mSocketConnector.getFilterChain().addLast("logger", loggingFilter);
}
mSocketConnector.getFilterChain().addLast("codec", mSocketConnector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new IcecastCodecFactory())); new ProtocolCodecFilter(new IcecastCodecFactory()));
@@ -148,6 +160,10 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
@Override @Override
public void run() public void run()
{ {
if(mVerboseLogging)
{
mLog.info("Attempting connection ...");
}
setBroadcastState(BroadcastState.CONNECTING); setBroadcastState(BroadcastState.CONNECTING);
try try
@@ -156,19 +172,42 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(), .connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
getBroadcastConfiguration().getPort())); getBroadcastConfiguration().getPort()));
if(mVerboseLogging)
{
mLog.info("Socket created - asynchronous connect requested - entering wait period");
}
boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
if(connected) if(connected)
{ {
if(mVerboseLogging)
{
mLog.info("Connected.");
}
mStreamingSession = future.getSession(); mStreamingSession = future.getSession();
mConnecting.set(false); mConnecting.set(false);
return; return;
} }
else
{
if(mVerboseLogging)
{
mLog.info("Not Connected. Connection attempt timeout [" + CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS + "ms] exceeded");
}
}
} }
catch(RuntimeIoException rioe) catch(RuntimeIoException rioe)
{ {
if(rioe.getCause() instanceof SocketException) if(rioe.getCause() instanceof SocketException)
{ {
if(mVerboseLogging)
{
mLog.info("Socket error. This usually indicates sdrtrunk can't reach the server " +
"address over the current network connection. Setting state to " +
"NETWORK UNAVAILABLE", rioe);
}
setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE); setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE);
mConnecting.set(false); mConnecting.set(false);
return; return;
@@ -176,12 +215,22 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
} }
catch(UnresolvedAddressException uae) catch(UnresolvedAddressException uae)
{ {
if(mVerboseLogging)
{
mLog.info("Unresolved Address error. This means the domain name services can't resolve " +
"the server URL to an IP address. Setting state to NETWORK UNAVAILABLE", uae);
}
setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE); setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE);
mConnecting.set(false); mConnecting.set(false);
return; return;
} }
catch(Exception e) catch(Exception e)
{ {
if(mVerboseLogging)
{
mLog.info("Unknown error. An error occurred while attempting to connect to the server.", e);
}
mLog.error("Error", e); mLog.error("Error", e);
//Disregard ... we'll disconnect and try again //Disregard ... we'll disconnect and try again
} }
@@ -190,6 +239,11 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
mLog.error("Throwable error caught", t); mLog.error("Throwable error caught", t);
} }
if(mVerboseLogging)
{
mLog.info("Starting disconnect sequence since an error occurred while trying to connect.");
}
disconnect(); disconnect();
mConnecting.set(false); mConnecting.set(false);
} }
@@ -209,6 +263,11 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
{ {
if(connected() && mStreamingSession != null) if(connected() && mStreamingSession != null)
{ {
if(mVerboseLogging)
{
mLog.info("Routine disconnect requested from a connected state with a non-null streaming session");
}
mStreamingSession.closeNow(); mStreamingSession.closeNow();
} }
else else
@@ -217,6 +276,12 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
//want to preserve the error state that got us here, so the user can see it. //want to preserve the error state that got us here, so the user can see it.
if(!getBroadcastState().isErrorState()) if(!getBroadcastState().isErrorState())
{ {
if(mVerboseLogging)
{
mLog.info("Disconnect requested - previous non-error state was [" + getBroadcastState() +
"] - changing state to DISCONNECTED");
}
setBroadcastState(BroadcastState.DISCONNECTED); setBroadcastState(BroadcastState.DISCONNECTED);
} }
@@ -282,12 +347,23 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
mInlineActive = false; mInlineActive = false;
} }
if(mVerboseLogging)
{
mLog.info("Session opened. Sending: " + sb);
}
session.write(sb.toString()); session.write(sb.toString());
} }
@Override @Override
public void sessionClosed(IoSession session) throws Exception public void sessionClosed(IoSession session) throws Exception
{ {
if(mVerboseLogging)
{
mLog.info("Session closed. Setting connecting flag to false.");
}
mLastConnectionAttempt = System.currentTimeMillis(); mLastConnectionAttempt = System.currentTimeMillis();
//If there is already an error state, don't override it. Otherwise, set state to disconnected //If there is already an error state, don't override it. Otherwise, set state to disconnected
@@ -311,15 +387,23 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
mLog.error("[" + getStreamName() + "] Broadcast error", cause); mLog.error("[" + getStreamName() + "] Broadcast error", cause);
} }
if(mVerboseLogging)
{
mLog.info("Session error caught.", cause);
}
disconnect(); disconnect();
} }
@Override @Override
public void messageReceived(IoSession session, Object object) throws Exception public void messageReceived(IoSession session, Object object) throws Exception
{ {
if(object instanceof String) if(object instanceof String message)
{ {
String message = (String) object; if(mVerboseLogging)
{
mLog.info("Message Received [" + message + "]");
}
if(message != null && !message.trim().isEmpty()) if(message != null && !message.trim().isEmpty())
{ {

View File

@@ -1,23 +1,20 @@
/* /*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* *
* * ****************************************************************************** * This program is free software: you can redistribute it and/or modify
* * Copyright (C) 2014-2020 Dennis Sheirer * it under the terms of the GNU General Public License as published by
* * * the Free Software Foundation, either version 3 of the License, or
* * This program is free software: you can redistribute it and/or modify * (at your option) any later version.
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/ */
package io.github.dsheirer.gui.playlist.streaming; package io.github.dsheirer.gui.playlist.streaming;
@@ -31,7 +28,9 @@ import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import org.controlsfx.control.ToggleSwitch;
/** /**
* Broadcastify streaming configuration editor * Broadcastify streaming configuration editor
@@ -39,6 +38,7 @@ import javafx.scene.layout.GridPane;
public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyFeedConfiguration> public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyFeedConfiguration>
{ {
private GridPane mEditorPane; private GridPane mEditorPane;
private ToggleSwitch mVerboseLoggingToggle;
private TextField mMountPointTextField; private TextField mMountPointTextField;
private IntegerTextField mFeedIdTextField; private IntegerTextField mFeedIdTextField;
@@ -54,16 +54,19 @@ public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyF
getFeedIdTextField().setDisable(item == null); getFeedIdTextField().setDisable(item == null);
getMountPointTextField().setDisable(item == null); getMountPointTextField().setDisable(item == null);
getVerboseLoggingToggle().setDisable(item == null);
if(item != null) if(item != null)
{ {
getFeedIdTextField().set(item.getFeedID()); getFeedIdTextField().set(item.getFeedID());
getMountPointTextField().setText(item.getMountPoint()); getMountPointTextField().setText(item.getMountPoint());
getVerboseLoggingToggle().setSelected(item.isVerboseLogging());
} }
else else
{ {
getFeedIdTextField().set(0); getFeedIdTextField().set(0);
getMountPointTextField().setText(null); getMountPointTextField().setText(null);
getVerboseLoggingToggle().setSelected(false);
} }
modifiedProperty().set(false); modifiedProperty().set(false);
@@ -76,6 +79,7 @@ public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyF
{ {
getItem().setFeedID(getFeedIdTextField().get()); getItem().setFeedID(getFeedIdTextField().get());
getItem().setMountPoint(getMountPointTextField().getText()); getItem().setMountPoint(getMountPointTextField().getText());
getItem().setVerboseLogging(getVerboseLoggingToggle().isSelected());
} }
super.save(); super.save();
@@ -178,11 +182,32 @@ public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyF
GridPane.setConstraints(getFeedIdTextField(), 1, 5); GridPane.setConstraints(getFeedIdTextField(), 1, 5);
getEditorPane().getChildren().add(getFeedIdTextField()); getEditorPane().getChildren().add(getFeedIdTextField());
Label loggingLabel = new Label("Verbose Logging");
GridPane.setHalignment(loggingLabel, HPos.RIGHT);
GridPane.setConstraints(loggingLabel, 2, 5);
getEditorPane().getChildren().add(loggingLabel);
GridPane.setConstraints(getVerboseLoggingToggle(), 3, 5);
getEditorPane().getChildren().add(getVerboseLoggingToggle());
} }
return mEditorPane; return mEditorPane;
} }
private ToggleSwitch getVerboseLoggingToggle()
{
if(mVerboseLoggingToggle == null)
{
mVerboseLoggingToggle = new ToggleSwitch();
mVerboseLoggingToggle.setTooltip(new Tooltip("Turn on additional logging for connection troubleshooting"));
mVerboseLoggingToggle.setDisable(true);
mVerboseLoggingToggle.selectedProperty().addListener((ob, ol, ne) -> modifiedProperty().set(true));
}
return mVerboseLoggingToggle;
}
private TextField getMountPointTextField() private TextField getMountPointTextField()
{ {
if(mMountPointTextField == null) if(mMountPointTextField == null)