Skip to content

Commit 4c4c89f

Browse files
authored
TEL-455: instrument call termination (#667)
1 parent b0e7f56 commit 4c4c89f

5 files changed

Lines changed: 115 additions & 64 deletions

File tree

pkg/sip/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func (c *Client) onBye(req *sip.Request, tx sip.ServerTransaction) bool {
349349
call.log.Infow("BYE from remote")
350350
go func(call *outboundCall) {
351351
call.cc.AcceptBye(req, tx)
352-
call.CloseWithReason(ctx, CallHangup, "bye", livekit.DisconnectReason_CLIENT_INITIATED)
352+
call.CloseWithReason(ctx, CallHangup, stats.Success("bye"), livekit.DisconnectReason_CLIENT_INITIATED)
353353
}(call)
354354
return true
355355
}

pkg/sip/inbound.go

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
403403
tauth()
404404
checked()
405405
if err != nil {
406-
cmon.InviteErrorShort("auth-error")
406+
cmon.InviteErrorShort(stats.ServerError("auth-error"))
407407
log.Warnw("Rejecting inbound, auth check failed", err)
408408
cc.RespondAndDrop(sip.StatusServiceUnavailable, "Try again later")
409409
return psrpc.NewError(psrpc.PermissionDenied, errors.Wrap(err, "rejecting inbound, auth check failed"))
@@ -431,22 +431,22 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
431431

432432
switch r.Result {
433433
case AuthDrop:
434-
cmon.InviteErrorShort("flood")
434+
cmon.InviteErrorShort(stats.ClientError("flood"))
435435
log.Debugw("Dropping inbound flood")
436436
cc.Drop()
437437
return psrpc.NewErrorf(psrpc.PermissionDenied, "call was not authorized by trunk configuration")
438438
case AuthNotFound:
439-
cmon.InviteErrorShort("no-rule")
439+
cmon.InviteErrorShort(stats.ClientError("no-rule"))
440440
log.Warnw("Rejecting inbound, doesn't match any Trunks", nil)
441441
cc.RespondAndDrop(sip.StatusNotFound, "Does not match any SIP Trunks")
442442
return psrpc.NewErrorf(psrpc.NotFound, "no trunk configuration for call")
443443
case AuthQuotaExceeded:
444-
cmon.InviteErrorShort("quota-exceeded")
444+
cmon.InviteErrorShort(stats.ClientError("quota-exceeded"))
445445
log.Warnw("Rejecting inbound, quota exceeded", nil)
446446
cc.RespondAndDrop(sip.StatusServiceUnavailable, "Service temporarily unavailable")
447447
return psrpc.NewErrorf(psrpc.ResourceExhausted, "quota limit exceeded")
448448
case AuthNoTrunkFound:
449-
cmon.InviteErrorShort("no-trunk")
449+
cmon.InviteErrorShort(stats.ClientError("no-trunk"))
450450
log.Warnw("Rejecting inbound, no trunk found", nil)
451451
cc.RespondAndDrop(sip.StatusNotFound, "No trunk found")
452452
return psrpc.NewErrorf(psrpc.NotFound, "no trunk found for call")
@@ -462,7 +462,7 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
462462
s.cmu.Lock()
463463
s.provisionalInvites.Add([2]string{cc.SIPCallID(), string(cc.Tag())}, cc.ID())
464464
s.cmu.Unlock()
465-
cmon.InviteErrorShort("unauthorized")
465+
cmon.InviteErrorShort(stats.ClientError("unauthorized"))
466466
// handleInviteAuth will generate the SIP Response as needed
467467
return psrpc.NewErrorf(psrpc.PermissionDenied, "invalid credentials were provided")
468468
}
@@ -697,7 +697,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
697697
c.mon.InviteAccept()
698698
c.mon.CallStart()
699699
defer c.mon.CallEnd()
700-
defer c.close(ctx, true, callDropped, "other")
700+
defer c.close(ctx, callDropped, stats.ServerError("other"))
701701

702702
// Extract and store the SIP call ID from the request
703703
if h := req.CallID(); h != nil {
@@ -750,22 +750,22 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
750750
err := fmt.Errorf("unexpected dispatch result: %v", disp.Result)
751751
c.log().Errorw("Rejecting inbound call", err)
752752
c.cc.RespondAndDrop(sip.StatusNotImplemented, "")
753-
c.close(ctx, true, callDropped, "unexpected-result")
753+
c.close(ctx, callDropped, stats.ServerError("unexpected-result"))
754754
return psrpc.NewError(psrpc.Unimplemented, err)
755755
case DispatchNoRuleDrop:
756756
c.log().Debugw("Rejecting inbound flood")
757757
c.cc.Drop()
758-
c.close(ctx, false, callFlood, "flood")
758+
c.close(ctx, callFlood, stats.ClientError("flood"))
759759
return psrpc.NewErrorf(psrpc.PermissionDenied, "call was not authorized by trunk configuration")
760760
case DispatchNoRuleReject:
761761
c.log().Infow("Rejecting inbound call, doesn't match any Dispatch Rules")
762762
c.cc.RespondAndDrop(sip.StatusNotFound, "Does not match Trunks or Dispatch Rules")
763-
c.close(ctx, false, callDropped, "no-dispatch")
763+
c.close(ctx, callDropped, stats.ClientError("no-dispatch"))
764764
return psrpc.NewErrorf(psrpc.NotFound, "no trunk configuration for call")
765765
case DispatchServiceUnavailable:
766766
c.log().Warnw("Rejecting inbound call, dispatch evaluation failed", nil)
767767
c.cc.RespondAndDrop(sip.StatusServiceUnavailable, "Try again later")
768-
c.close(ctx, true, callDropped, "dispatch-error")
768+
c.close(ctx, callDropped, stats.ServerError("dispatch-error"))
769769
return psrpc.NewErrorf(psrpc.Unavailable, "dispatch rule evaluation unavailable")
770770
case DispatchAccept:
771771
pinPrompt = false
@@ -795,15 +795,15 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
795795
if err != nil {
796796
sipReason := sip.StatusInternalServerError
797797
log = log.WithValues("sdp", string(rawSDP))
798-
status, reason := callDropped, "media-failed"
798+
status, term := callDropped, stats.ServerError("media-failed")
799799
if errors.Is(err, sdp.ErrNoCommonMedia) {
800-
status, reason = callMediaFailed, "no-common-codec"
800+
status, term = callMediaFailed, stats.ClientError("no-common-codec")
801801
sipReason = sip.StatusBadRequest
802802
} else if errors.Is(err, sdp.ErrNoCommonCrypto) {
803-
status, reason = callMediaFailed, "no-common-crypto"
803+
status, term = callMediaFailed, stats.ClientError("no-common-crypto")
804804
sipReason = sip.StatusBadRequest
805805
} else if e := (SDPError{}); errors.As(err, &e) {
806-
status, reason = callMediaFailed, "sdp-error"
806+
status, term = callMediaFailed, stats.ClientError("sdp-error")
807807
sipReason = sip.StatusBadRequest
808808
}
809809
if sipReason >= 500 {
@@ -812,7 +812,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
812812
log.Warnw("Cannot start media", err)
813813
}
814814
c.cc.RespondAndDrop(sipReason, "")
815-
c.close(ctx, true, status, reason)
815+
c.close(ctx, status, term)
816816
return nil, err
817817
}
818818
return answerData, nil
@@ -844,7 +844,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
844844
return false, err
845845
} else if err != nil {
846846
c.log().Errorw("Cannot accept the call", err)
847-
c.close(ctx, true, callAcceptFailed, "accept-failed")
847+
c.close(ctx, callAcceptFailed, stats.ServerError("accept-failed"))
848848
return false, err
849849
}
850850
if !c.s.conf.Experimental.InboundWaitACK {
@@ -909,7 +909,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, tid traceid.ID, req *sip
909909
// Publish our own track.
910910
if err := c.publishTrack(); err != nil {
911911
c.log().Errorw("Cannot publish track", err)
912-
c.close(ctx, true, callDropped, "publish-failed")
912+
c.close(ctx, callDropped, stats.ServerError("publish-failed"))
913913
return errors.Wrap(err, "publishing track to room failed")
914914
}
915915
tsub := c.mon.StageDurTimer("track-subscribe")
@@ -966,7 +966,7 @@ func (c *inboundCall) waitForCallEnd(ctx context.Context, ackReceived <-chan str
966966
c.state.DeferUpdate(func(info *livekit.SIPCallInfo) {
967967
info.DisconnectReason = livekit.DisconnectReason_CLIENT_INITIATED
968968
})
969-
c.close(ctx, false, callDropped, "removed")
969+
c.close(ctx, callDropped, stats.Success("removed"))
970970
return nil
971971
case <-c.media.Timeout():
972972
return c.mediaTimeout(ctx)
@@ -1096,7 +1096,7 @@ func (c *inboundCall) waitSubscribe(ctx context.Context, timeout time.Duration)
10961096
case <-c.media.Timeout():
10971097
return false, c.mediaTimeout(ctx)
10981098
case <-timer.C:
1099-
c.close(ctx, false, callDropped, "cannot-subscribe")
1099+
c.close(ctx, callDropped, stats.ServerError("cannot-subscribe"))
11001100
return false, psrpc.NewErrorf(psrpc.DeadlineExceeded, "room subscription timed out")
11011101
case <-c.lkRoom.Subscribed():
11021102
return true, nil
@@ -1152,13 +1152,13 @@ func (c *inboundCall) pinPrompt(ctx context.Context, trunkID string) (disp CallD
11521152
}
11531153
if disp.Result == DispatchServiceUnavailable {
11541154
c.log().Warnw("Rejecting call, dispatch evaluation failed", nil, "pin", pin, "noPin", noPin)
1155-
c.close(ctx, true, callDropped, "dispatch-error")
1155+
c.close(ctx, callDropped, stats.ServerError("dispatch-error"))
11561156
return disp, false, psrpc.NewErrorf(psrpc.Unavailable, "dispatch rule evaluation unavailable")
11571157
}
11581158
if disp.Result != DispatchAccept || disp.Room.RoomName == "" {
11591159
c.log().Infow("Rejecting call", "pin", pin, "noPin", noPin)
11601160
c.playAudio(ctx, c.s.res.wrongPin)
1161-
c.close(ctx, false, callDropped, "wrong-pin")
1161+
c.close(ctx, callDropped, stats.ClientError("wrong-pin"))
11621162
return disp, false, psrpc.NewErrorf(psrpc.PermissionDenied, "wrong pin")
11631163
}
11641164
c.playAudio(ctx, c.s.res.roomJoin)
@@ -1168,7 +1168,7 @@ func (c *inboundCall) pinPrompt(ctx context.Context, trunkID string) (disp CallD
11681168
pin += string(b.Digit)
11691169
if len(pin) > pinLimit {
11701170
c.playAudio(ctx, c.s.res.wrongPin)
1171-
c.close(ctx, false, callDropped, "wrong-pin")
1171+
c.close(ctx, callDropped, stats.ClientError("wrong-pin"))
11721172
return disp, false, psrpc.NewErrorf(psrpc.PermissionDenied, "wrong pin")
11731173
}
11741174
}
@@ -1180,7 +1180,7 @@ func (c *inboundCall) printStats(log logger.Logger) {
11801180
}
11811181

11821182
// close should only be called from handleInvite.
1183-
func (c *inboundCall) close(ctx context.Context, error bool, status CallStatus, reason string) {
1183+
func (c *inboundCall) close(ctx context.Context, status CallStatus, t stats.Termination) {
11841184
termCtx, cancel := context.WithCancel(context.Background()) // Do not use ctx
11851185
defer cancel()
11861186
go func() {
@@ -1200,15 +1200,15 @@ func (c *inboundCall) close(ctx context.Context, error bool, status CallStatus,
12001200
defer c.mon.StageDurTimer("close")
12011201
c.stats.Closed.Store(true)
12021202
sipCode, sipStatus := status.SIPStatus()
1203-
log := c.log().WithValues("status", sipCode, "reason", reason)
1203+
log := c.log().WithValues("status", sipCode, "result", string(t.Result), "reason", t.Reason)
12041204
defer func() {
12051205
c.stats.Update()
12061206
c.printStats(log)
12071207
c.sigTs.Log(log)
12081208
}()
12091209
c.setStatus(status)
1210-
c.mon.CallTerminate(reason)
1211-
isWarn := error || status == callHangupMedia
1210+
c.mon.CallTerminate(t)
1211+
isWarn := t.Result == stats.ResultServerError || status == callHangupMedia
12121212
if isWarn {
12131213
log.Warnw("Closing inbound call with error", nil)
12141214
} else {
@@ -1243,7 +1243,7 @@ func (c *inboundCall) close(ctx context.Context, error bool, status CallStatus,
12431243
ProjectID: c.projectID,
12441244
CallID: c.call.LkCallId,
12451245
SipCallID: c.call.SipCallId,
1246-
}, c.state.callInfo, reason)
1246+
}, c.state.callInfo, t.Reason)
12471247
}(c.tid)
12481248
}
12491249

@@ -1255,30 +1255,30 @@ func (c *inboundCall) closeWithTimeout(ctx context.Context, isError bool) {
12551255
if !isError {
12561256
status = callHangupMedia
12571257
}
1258-
c.close(ctx, isError, status, "media-timeout")
1258+
c.close(ctx, status, stats.ServerError("media-timeout"))
12591259
}
12601260

12611261
func (c *inboundCall) closeWithNoACK(ctx context.Context) {
1262-
c.close(ctx, true, callNoACK, "no-ack")
1262+
c.close(ctx, callNoACK, stats.ServerError("no-ack"))
12631263
}
12641264

12651265
func (c *inboundCall) closeWithCancelled(ctx context.Context) {
12661266
var reason ReasonHeader
12671267
if p := c.closeReason.Load(); p != nil {
12681268
reason = *p
12691269
}
1270-
c.closeWithReason(ctx, CallHangup, "cancelled", reason)
1270+
c.closeWithReason(ctx, CallHangup, stats.Success("cancelled"), reason)
12711271
}
12721272

12731273
func (c *inboundCall) closeWithHangup(ctx context.Context) {
12741274
var reason ReasonHeader
12751275
if p := c.closeReason.Load(); p != nil {
12761276
reason = *p
12771277
}
1278-
c.closeWithReason(ctx, CallHangup, "hangup", reason)
1278+
c.closeWithReason(ctx, CallHangup, stats.Success("hangup"), reason)
12791279
}
12801280

1281-
func (c *inboundCall) closeWithReason(ctx context.Context, status CallStatus, reasonName string, reason ReasonHeader) {
1281+
func (c *inboundCall) closeWithReason(ctx context.Context, status CallStatus, t stats.Termination, reason ReasonHeader) {
12821282
ctx = context.WithoutCancel(ctx)
12831283
c.state.DeferUpdate(func(info *livekit.SIPCallInfo) {
12841284
info.DisconnectReason = livekit.DisconnectReason_CLIENT_INITIATED
@@ -1290,10 +1290,10 @@ func (c *inboundCall) closeWithReason(ctx context.Context, status CallStatus, re
12901290
})
12911291
if reason.Type != "" {
12921292
if !reason.IsNormal() {
1293-
reasonName = fmt.Sprintf("bye-%s-%d", strings.ToLower(reason.Type), reason.Cause)
1293+
t.Reason = fmt.Sprintf("bye-%s-%d", strings.ToLower(reason.Type), reason.Cause)
12941294
}
12951295
}
1296-
c.close(ctx, false, status, reasonName)
1296+
c.close(ctx, status, t)
12971297
}
12981298

12991299
func (c *inboundCall) Bye(reason ReasonHeader) {
@@ -1390,7 +1390,7 @@ func (c *inboundCall) joinRoom(ctx context.Context, rconf RoomConfig, status Cal
13901390
c.log().Infow("Joining room")
13911391
if err := c.createLiveKitParticipant(ctx, rconf, status); err != nil {
13921392
c.log().Errorw("Cannot create LiveKit participant", err)
1393-
c.close(ctx, true, callDropped, "participant-failed")
1393+
c.close(ctx, callDropped, stats.ServerError("participant-failed"))
13941394
return errors.Wrap(err, "cannot create LiveKit participant")
13951395
}
13961396
return nil

0 commit comments

Comments
 (0)