Been recently exploring Azure SignalR as a Service. I'm very confused.
I've got the following Hub written in serverless:
public class MyModel
{
public string Username { get; set; }
}
public class MyHub : ServerlessHub
{
[FunctionName("negotiate")]
public IActionResult Negotiate([HttpTrigger(Microsoft.Azure.WebJobs.Extensions.Http.AuthorizationLevel.Anonymous, "get")] MyModel req,
[SignalRConnectionInfo(HubName = "MyHub")] SignalRConnectionInfo connectionInfo,
ILogger log)
{
log.LogInformation(connectionInfo.Url + ",," + connectionInfo.AccessToken);
return new OkObjectResult(Negotiate());
}
[FunctionName(nameof(OnConnected))]
public async Task OnConnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
{
logger.LogInformation($"{invocationContext.ConnectionId} has connected");
}
[FunctionName(nameof(OnDisconnected))]
public async Task OnDisconnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
{
logger.LogInformation($"{invocationContext.ConnectionId} has disconnected");
}
}
Client code:
<script>
let messages = $('#messages');
const connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7111/api")
.configureLogging(signalR.LogLevel.Information)
.build()
connection.on("OrderEvent", (order, msg) => {
console.log('new message' + order + msg);
});
connection
.start()
.catch(console.error);
</script>
The client connects but
OnConnected doesn't log in my Azure function window
OnDisconnected doesn't log in my Azure function window
How do I send direct messages to a client?
How do I get a subset of clients to send a message to? Eg groups?
At the moment, whoever gets connected to my hub gets all the updates.
Related
After I send a message to a topic on Azure Service Bus using Spring Integration I would like to get the message id Azure generates. I can do this using JMS. Is there a way to do this using Spring Integration? The code I'm working with:
#Service
public class ServiceBusDemo {
private static final String OUTPUT_CHANNEL = "topic.output";
private static final String TOPIC_NAME = "my_topic";
#Autowired
TopicOutboundGateway messagingGateway;
public String send(String message) {
// How can I get the Azure message id after sending here?
this.messagingGateway.send(message);
return message;
}
#Bean
#ServiceActivator(inputChannel = OUTPUT_CHANNEL)
public MessageHandler topicMessageSender(ServiceBusTopicOperation topicOperation) {
DefaultMessageHandler handler = new DefaultMessageHandler(TOPIC_NAME, topicOperation);
handler.setSendCallback(new ListenableFutureCallback<>() {
#Override
public void onSuccess(Void result) {
System.out.println("Message was sent successfully to service bus.");
}
#Override
public void onFailure(Throwable ex) {
System.out.println("There was an error sending the message to service bus.");
}
});
return handler;
}
#MessagingGateway(defaultRequestChannel = OUTPUT_CHANNEL)
public interface TopicOutboundGateway {
void send(String text);
}
}
You could use ChannelInterceptor to get message headers:
public class CustomChannelInterceptor implements ChannelInterceptor {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
//key of the message-id header is not stable, you should add logic here to check which header key should be used here.
//ref: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/spring/azure-spring-cloud-starter-servicebus#support-for-service-bus-message-headers-and-properties
String messageId = message.getHeaders().get("message-id-header-key").toString();
return ChannelInterceptor.super.preSend(message, channel);
}
}
Then in the configuration, set this interceptor to your channel
#Bean(name = OUTPUT_CHANNEL)
public BroadcastCapableChannel pubSubChannel() {
PublishSubscribeChannel channel = new PublishSubscribeChannel();
channel.setInterceptors(Arrays.asList(new CustomChannelInterceptor()));
return channel;
}
I am working on a project where I want to implement Service Buss trigger in Web Job. I have followed the instructions here:
https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-get-started.
public class Functions
{
public static void ProcessQueueMessage([QueueTrigger("queue")] string message, ILogger logger)
{
logger.LogInformation(message);
}
}
But instead of storage queue (QueueTrigger) I want to use ServiceBus' Microsoft.Azure.WebJobs.ServiceBusTrigger. In the documentation states to use the following:
https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to#service-bus-trigger-configuration-version-3x
static void Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus(sbOptions =>
{
sbOptions.MessageHandlerOptions.AutoComplete = true;
sbOptions.MessageHandlerOptions.MaxConcurrentCalls = 16;
});
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
However, the problem is that b.AddServiceBus is not even available (I have the latest Web Jobs version). So, when I run the project, I get "No job functions found" error. Any ideas or pointers?
I did try:
public static void ProcessQueueMessage([Microsoft.Azure.WebJobs.ServiceBusTrigger("queue")] string message, ILogger logger)
{
logger.LogInformation(message);
}
and
public static void Run([ServiceBusTrigger("queue", AccessRights.Manage, Connection = "Endpoint=bla bla")]
string myQueueItem, Int32 deliveryCount, DateTime enqueuedTimeUtc, string messageId,ILogger log)
{
}
Here are packages you need.
Microsoft.Azure.WebJobs(>= 3.0.10)
Microsoft.Azure.WebJobs.Extensions
Microsoft.Azure.WebJobs.Extensions.ServiceBus
Microsoft.Azure.WebJobs.ServiceBus
Microsoft.Azure.WebJobs.Extensions.ServiceBus this package is used to let you use b.AddServiceBus() method and the Microsoft.Azure.WebJobs.ServiceBus is used to create the ServiceBusTrigger.
The below is my code, you could have a test.
public static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
Functions.cs
public static void processservicebus(
[ServiceBusTrigger("test", Connection = "ServiceBusConnection")]string myQueueItem,
ILogger log)
{
log.LogInformation(myQueueItem);
}
Do You have any idea how to log all outgoing/incoming messages? I am not sure how to capture outgoing messages.
I use Chains and Forms.
For example
await Conversation.SendAsync(activity, rootDialog.BuildChain);
AND
activity.CreateReply(.....);
I found better solution
public class BotToUserLogger : IBotToUser
{
private readonly IMessageActivity _toBot;
private readonly IConnectorClient _client;
public BotToUserLogger(IMessageActivity toBot, IConnectorClient client)
{
SetField.NotNull(out _toBot, nameof(toBot), toBot);
SetField.NotNull(out _client, nameof(client), client);
}
public IMessageActivity MakeMessage()
{
var toBotActivity = (Activity)_toBot;
return toBotActivity.CreateReply();
}
public async Task PostAsync(IMessageActivity message, CancellationToken cancellationToken = default(CancellationToken))
{
await _client.Conversations.ReplyToActivityAsync((Activity)message, cancellationToken);
}
}
public class BotToUserDatabaseWriter : IBotToUser
{
private readonly IBotToUser _inner;
public BotToUserDatabaseWriter(IBotToUser inner)
{
SetField.NotNull(out _inner, nameof(inner), inner);
}
public IMessageActivity MakeMessage()
{
return _inner.MakeMessage();
}
public async Task PostAsync(IMessageActivity message, CancellationToken cancellationToken = default(CancellationToken))
{
// loging outgoing message
Debug.WriteLine(message.Text);
//TODO log message for example into DB
await _inner.PostAsync(message, cancellationToken);
}
In controller use
public MessagesController()
{
var builder = new ContainerBuilder();
builder.RegisterType<BotToUserLogger>()
.AsSelf()
.InstancePerLifetimeScope();
builder.Register(c => new BotToUserTextWriter(c.Resolve<BotToUserLogger>()))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
builder.Update(Microsoft.Bot.Builder.Dialogs.Conversation.Container);
}
Its look like I cant log outgoing message.
I changed SDK source code.
Add event in Conversations.cs
For example like this.
public delegate void MessageSendedEventHandler(object sender, Activity activity, string conversationId);
public static event MessageSendedEventHandler MessageSended;
And add in every Send....HttpMessagesAsync method
this MessageSended?.Invoke(this, activity, conversationId);
its not great solution. But its working
I am currently working on Internet Of Things, in my current project I was Created the One Azure Cloud Service Project in that I Created the Worker Role, inside the worker role i have wrote below lines of code.
public class WorkerRole : RoleEntryPoint
{
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
private static string connectionString;
private static string eventHubName;
public static ServiceClient iotHubServiceClient { get; private set; }
public static EventHubClient eventHubClient { get; private set; }
public override void Run()
{
Trace.TraceInformation("EventsForwarding Run()...\n");
try
{
this.RunAsync(this.cancellationTokenSource.Token).Wait();
}
finally
{
this.runCompleteEvent.Set();
}
}
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
bool result = base.OnStart();
Trace.TraceInformation("EventsForwarding OnStart()...\n");
connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
eventHubName = ConfigurationManager.AppSettings["Microsoft.ServiceBus.EventHubName"];
string storageAccountName = ConfigurationManager.AppSettings["AzureStorage.AccountName"];
string storageAccountKey = ConfigurationManager.AppSettings["AzureStorage.Key"];
string storageAccountString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
storageAccountName, storageAccountKey);
string iotHubConnectionString = ConfigurationManager.AppSettings["AzureIoTHub.ConnectionString"];
iotHubServiceClient = ServiceClient.CreateFromConnectionString(iotHubConnectionString);
eventHubClient = EventHubClient.CreateFromConnectionString(connectionString, eventHubName);
var defaultConsumerGroup = eventHubClient.GetDefaultConsumerGroup();
string eventProcessorHostName = "SensorEventProcessor";
EventProcessorHost eventProcessorHost = new EventProcessorHost(eventProcessorHostName, eventHubName, defaultConsumerGroup.GroupName, connectionString, storageAccountString);
eventProcessorHost.RegisterEventProcessorAsync<SensorEventProcessor>().Wait();
Trace.TraceInformation("Receiving events...\n");
return result;
}
public override void OnStop()
{
Trace.TraceInformation("EventsForwarding is OnStop()...");
this.cancellationTokenSource.Cancel();
this.runCompleteEvent.WaitOne();
base.OnStop();
Trace.TraceInformation("EventsForwarding has stopped");
}
private async Task RunAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Trace.TraceInformation("EventsToCommmandsService running...\n");
await Task.Delay(1000);
}
}
}
Next I have wrote the below lines of code in SensorEventProcessor, for receiving the messages from event hub and send those messages to IoT hub.
class SensorEventProcessor : IEventProcessor
{
Stopwatch checkpointStopWatch;
PartitionContext partitionContext;
public async Task CloseAsync(PartitionContext context, CloseReason reason)
{
Trace.TraceInformation(string.Format("EventProcessor Shuting Down. Partition '{0}', Reason: '{1}'.", this.partitionContext.Lease.PartitionId, reason.ToString()));
if (reason == CloseReason.Shutdown)
{
await context.CheckpointAsync();
}
}
public Task OpenAsync(PartitionContext context)
{
Trace.TraceInformation(string.Format("Initializing EventProcessor: Partition: '{0}', Offset: '{1}'", context.Lease.PartitionId, context.Lease.Offset));
this.partitionContext = context;
this.checkpointStopWatch = new Stopwatch();
this.checkpointStopWatch.Start();
return Task.FromResult<object>(null);
}
public async Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
Trace.TraceInformation("\n");
Trace.TraceInformation("........ProcessEventsAsync........");
//string commandParameterNew = "{\"Name\":\"AlarmThreshold\",\"Parameters\":{\"SensorId\":\"" + "Hello World" + "\"}}";
//await WorkerRole.iotHubServiceClient.SendAsync("astranidevice", new Microsoft.Azure.Devices.Message(Encoding.UTF8.GetBytes(commandParameterNew)));
foreach (EventData eventData in messages)
{
try
{
string jsonString = Encoding.UTF8.GetString(eventData.GetBytes());
Trace.TraceInformation(string.Format("Message received at '{0}'. Partition: '{1}'",
eventData.EnqueuedTimeUtc.ToLocalTime(), this.partitionContext.Lease.PartitionId));
Trace.TraceInformation(string.Format("-->Raw Data: '{0}'", jsonString));
SimpleTemperatureAlertData newSensorEvent = this.DeserializeEventData(jsonString);
Trace.TraceInformation(string.Format("-->Serialized Data: '{0}', '{1}', '{2}', '{3}', '{4}'",
newSensorEvent.Time, newSensorEvent.RoomTemp, newSensorEvent.RoomPressure, newSensorEvent.RoomAlt, newSensorEvent.DeviceId));
// Issuing alarm to device.
string commandParameterNew = "{\"Name\":\"AlarmThreshold\",\"Parameters\":{\"SensorId\":\"" + "Hello World" + "\"}}";
Trace.TraceInformation("Issuing alarm to device: '{0}', from sensor: '{1}'", newSensorEvent.DeviceId, newSensorEvent.RoomTemp);
Trace.TraceInformation("New Command Parameter: '{0}'", commandParameterNew);
await WorkerRole.iotHubServiceClient.SendAsync(newSensorEvent.DeviceId, new Microsoft.Azure.Devices.Message(Encoding.UTF8.GetBytes(commandParameterNew)));
}
catch (Exception ex)
{
Trace.TraceInformation("Error in ProssEventsAsync -- {0}\n", ex.Message);
}
}
await context.CheckpointAsync();
}
private SimpleTemperatureAlertData DeserializeEventData(string eventDataString)
{
return JsonConvert.DeserializeObject<SimpleTemperatureAlertData>(eventDataString);
}
}
When I was debug my code, the ProcessEventsAsync(PartitionContext context, IEnumerable messages) method will never call and just enter into OpenAsync() method then itstop the debugging.
Please tell me Where I did mistake in my project and tell me when the ProcessEventsAsync() method will call.
Regards,
Pradeep
IEventProcessor.ProcessEventsAsync is invoked when there are any unprocessed messages in the EventHub.
An Event Hub contains multiple partitions. A partition is an ordered sequence of events. Within a partition, each event includes an offset. This offset is used by consumers (IEventProcessor) to show the location in the event sequence for a given partition. When an IEventProcessor connects (EventProcessorHost.RegisterEventProcessorAsync), it passes this offset to the Event Hub to specify the location at which to start reading. When there are unprocessed messages (events with higher offset), they are delivered to the IEventProcessor. Checkpointing is used to persist the offset of processed messages (PartitionContext.CheckpointAsync).
You can find detailed information about the internals of EventHub: Azure Event Hubs overview
Have you sent any messages to the EventHub (EventHubClient.SendAsync(EventData))?
I am working with Azure web jobs. Also I am aware that the TextWriter is used to write logs in case of web jobs (VS 2013). However, The logs are created under the Output logs folder under the blob container. THese are not user friendly. I have to open each file to read the message written to it.
Is there any way to change the logging to table, which is user friendly to read?
Thanks in advance.
I'm not sure if there's a "native" way to do this, but you can add Azure Storage Client through nuget and write your own "Log To Azure Tables".
You can use the Semantic Logging Application Block for Windows Azure.
It allows you to log into an Azure Table Storage.
Define your Eventsource:
// A simple interface to log what you need ...
public interface ILog
{
void Debug(string message);
void Info(string message);
void Warn(string message);
void Error(string message);
void Error(string message, Exception exception);
}
Implement the interface :
And the implementation ( implementation of your interface must be decorated with the NonEventAttribute see this post) :
[EventSource(Name = "MyLogEventsource")]
public class Log : EventSource, ILog
{
public Log()
{
EventSourceAnalyzer.InspectAll(this);
}
[NonEvent]
public void Debug(string message)
{
DebugInternal(message);
}
[Event(1)]
private void DebugInternal(string message)
{
WriteEvent(1, message);
}
[NonEvent]
public void Info(string message)
{
InfoInternal(message);
}
[Event(2)]
private void InfoInternal(string message)
{
WriteEvent(2, message);
}
[NonEvent]
public void Warn(string message)
{
WarnInternal(message);
}
[Event(3)]
private void WarnInternal(string message)
{
WriteEvent(3, message);
}
[NonEvent]
public void Error(string message)
{
ErrorInternal(message, "", "");
}
[NonEvent]
public void Error(string message, Exception exception)
{
ErrorInternal(message, exception.Message, exception.ToString());
}
[Event(4)]
private void ErrorInternal(string message, string exceptionMessage, string exceptionDetails)
{
WriteEvent(4, message, exceptionMessage, exceptionDetails);
}
}
Now you can register your event source like that :
var log = new Log();
var eventListeners = new List<ObservableEventListener>();
// Log to Azure Table
var azureListener = new ObservableEventListener();
azureListener.EnableEvents(log , EventLevel.LogAlways, Keywords.All);
azureListener.LogToWindowsAzureTable(
instanceName: Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID") ?? "DevelopmentInstance",
connectionString: CloudConfigurationManager.GetSetting("MyStorageConnectionString")
tableAddress: "MyLogTable");
eventListeners .Add(azureListener);