/// <summary>
        /// Logs an incoming message to the Bot. Core channel information is stored along with the original message and sentiment.
        /// Logs an event called "BotMessageReceived".
        /// Logs the following properties: conversation id, from user id, conversation name, channel id, client info.
        /// Other info logged based on configuration: from user name, original message, sentiment.
        /// </summary>
        /// <param name="activity">The incoming Activity from the Bot Framework.</param>
        /// <param name="additionalProperties">Additional properties to log.</param>
        public void LogIncomingMessage(ITurnContext context, Dictionary <string, string> additionalProperties = null)
        {
            // Simple guard statements
            EnsureNotNull(context, "Context activity cannot be null");

            // Track the event with an exception wrapper
            TrackEventWithExceptionWrapper((TelemetryClient tc) =>
            {
                var activity = context.Activity.AsMessageActivity();
                Dictionary <string, string> telemetryProperties = new Dictionary <string, string>();

                // Set the Telemetry context as we don't have a BF Telemetry Initializer at this time...
                if (!string.IsNullOrEmpty(activity.Conversation.Id))
                {
                    tc.Context.Session.Id = activity.Conversation.Id;
                }

                if (!string.IsNullOrEmpty(activity.From.Id))
                {
                    tc.Context.User.Id = activity.From.Id;
                }

                // General message metadata, defensive to cover for discrepancies across channels
                if (!string.IsNullOrEmpty(activity.ChannelId))
                {
                    telemetryProperties.Add(TelemetryHelper.ChannelProperty, activity.ChannelId);
                }

                if (!string.IsNullOrEmpty(activity.From.Id))
                {
                    telemetryProperties.Add(TelemetryHelper.FromIdProperty, activity.From.Id);
                }

                if (!string.IsNullOrEmpty(activity.Conversation.Id))
                {
                    telemetryProperties.Add(TelemetryHelper.ConversationIdProperty, activity.Conversation.Id);
                }

                if (!string.IsNullOrEmpty(activity.Conversation.Name))
                {
                    telemetryProperties.Add(TelemetryHelper.ConversationNameProperty, activity.Conversation.Name);
                }

                if (additionalProperties != null)
                {
                    foreach (KeyValuePair <string, string> valuePair in additionalProperties)
                    {
                        if (telemetryProperties.ContainsKey(valuePair.Key))
                        {
                            telemetryProperties[valuePair.Key] = valuePair.Value;
                        }
                        else
                        {
                            telemetryProperties.Add(valuePair.Key, valuePair.Value);
                        }
                    }
                }

                // Now check for specific entities such as client info...
                if (activity.Entities != null)
                {
                    // Do we have any client info? e.g. language, locale, device type
                    var clientEntity = activity.Entities.FirstOrDefault(e => e.Type == TelemetryHelper.ClientInfoIdentifier);

                    if (clientEntity != null && clientEntity.Properties != null)
                    {
                        string prop = clientEntity.Properties.ToString();
                        telemetryProperties.Add(TelemetryHelper.ClientInfoProperty, prop);
                    }
                }

                // For some customers, logging user name within Application Insights might be an issue so have provided a config setting to disable this feature
                if (logUserName && !string.IsNullOrEmpty(activity.From.Name))
                {
                    telemetryProperties.Add(TelemetryHelper.FromNameProperty, activity.From.Name);
                }

                // For some customers, logging the utterances within Application Insights might be an so have provided a config setting to disable this feature
                if (logOriginalMessages && !string.IsNullOrEmpty(activity.Text))
                {
                    telemetryProperties.Add(TelemetryHelper.TextProperty, activity.Text);
                }

                // Only perform Text Analytics operations if we have a valid key and log sentiment is set to true
                if (!string.IsNullOrEmpty(textAnalyticsKey) && logSentimentAndKeyPhrases && !string.IsNullOrEmpty(activity.Text))
                {
                    try
                    {
                        // Crude but gets a good sense of the utterance length.
                        // NOTE: before splitting, replace multiple instances of spaces with a single space
                        // and trim either end, so that we do not skew the amount of words in the trimmed list.
                        string modifiedText = Regex.Replace(activity.Text, @"\s+", " ").Trim();
                        string[] words      = activity.Text.Split(' ');

                        // Sentiment and Key Phrase extraction is not effective on short utterances so we skip if less than the provided threshold
                        if (words.Length >= sentimentWordThreshold)
                        {
                            // For each utterance we identify the language in order to then extract key-phrases and sentiment which is addded to the Telemetry
                            // LUIS now performs Key Phrase Extraction and Sentiment for you but not exposed as part of the v4SDK, once fixed this can be removed.
                            var(identifiedLanguage, keyPhrases, sentiment) = TextAnalyticsHelper.EvaluateUtterance(textAnalyticsClient, activity.Text);

                            if (!string.IsNullOrEmpty(identifiedLanguage))
                            {
                                telemetryProperties.Add(TelemetryHelper.LanguageProperty, identifiedLanguage);
                            }

                            if (!string.IsNullOrEmpty(keyPhrases))
                            {
                                telemetryProperties.Add(TelemetryHelper.KeyPhrasesProperty, keyPhrases);
                            }

                            if (sentiment != int.MinValue)
                            {
                                string sentimentScore = sentiment.ToString("N2");
                                telemetryProperties.Add(TelemetryHelper.SentimentProperty, sentimentScore);
                            }
                        }
                        else
                        {
                            tc.TrackTrace($"TelemetryHelper::LogIncomingMessage::No sentiment calculated for a utterance with {words.Length} word(s).");
                        }
                    }
                    catch (Exception e)
                    {
                        tc.TrackException(e);
                        tc.TrackTrace($"TelemetryHelper::Exception ocurred whilst calculating sentiment - skipping but still logging without it. {e.Message}");
                    }
                }

                // Log the event
                tc.TrackEvent(TelemetryHelper.BotMessageReceivedEvent, telemetryProperties);
            });
        }