Skip to content

Conversation

scbjans
Copy link
Contributor

@scbjans scbjans commented Jul 9, 2025

Largely based on prior work in #1432 by @Hareramrai and @jdehaan

Since that PR seems stale, I've updated it with some small edits to comply with release management using toys.

Should fix: github.com//issues/669

yield(payload).tap do |response|
annotate_span_with_response!(span, response)
end
rescue StandardError => e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't in_span already handle Error Recording and setting the status?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does, but it doesn't add the ldap.error_message attribute. I've updated this a bit, and in 0583992 we'll rescue specifically on a ::Net::LDAP::Error

LDAP::Instrumentation.instance.config
end

def annotate_span_with_response!(span, response)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only add a ! if you have an existing method that does not have a !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


return if ::Net::LDAP::ResultCodesNonError.include?(status_code)

span.record_exception(error_message) unless error_message.empty?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe record_exception is an explicit helper for exception events. Consider using the Span Status Code description instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

OpenTelemetry::SemanticConventions::Trace::NET_PEER_NAME => host || hosts,
OpenTelemetry::SemanticConventions::Trace::NET_PEER_PORT => port,
OpenTelemetry::SemanticConventions::Trace::PEER_SERVICE => instrumentation_config[:peer_service]
}.merge!(OpenTelemetry::Common::HTTP::ClientContext.attributes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this amending HTTP Client attributes? How is that relevant to LDAP lookups?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I just took the already existing stale PR that originally was opened to add this instrumentation. That did the job for me, and I thought it might be beneficial for others to have this instrumentation more easily available.
I rushed this a bit and skipped the due diligence (sorry!), so I'm glad you ask.

It doesn't: I removed it.

@encryption = args[:encryption]
end

def instrument(event, payload)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want more specific attribute mappers here for each of these events?

https://github.com/search?q=repo%3Aruby-ldap%2Fruby-net-ldap+%2Finstrument+%22%2F&type=code

I do not particularly like the practice of dumping json strings where we do not know if the contents of the provided payload have sensitive information in them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.

My naive thought was that it would be handled in net-ldap itself, but upon closer inspection I see that this instrumentation can leak e.g. passwords in cleartext.
I'll need some time to figure out which specific attributes to include.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and added test in 3d2ff26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a good starting point would be to capture the name of the operation being performed ie open, read, parse_pdu.


tracer.in_span(
event,
attributes: attributes.compact,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allocates and extra hash object.

lets avoid that by checking if the values are present before adding them to the hash.

tracer.in_span(
event,
attributes: attributes.compact,
kind: :client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think LDAP qualifies as RPC? Would using RPC client semantics be right for this?
Would using ECS style events make more sense for these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was inclined to say LDAP qualifies as RPC, but my AI friend does not agree with me. Apparently LDAP itself is not RPC, but it is often combined with RPC (e.g. when joining computers to Windows domains), but we're only instrumenting LDAP in this case. Semconv doesn't specify anything for ldap, and other languages don't seem to have ldap instrumentation.

I'm not sure what to do here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points. I was also wondering about SemConv as I'm reading through this. I asked the #opentelemetry-semantic-conventions channel on CNCF Slack for advice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heard back from the thread. There aren't any specific LDAP conventions we should adhere to. Instead, they suggested we use attributes from other relevant categories like network and server.

@scbjans scbjans marked this pull request as draft July 9, 2025 14:30
def initialize(args = {})
super

@instrumentation_service = args[:instrumentation_service] || OpenTelemetry::Instrumentation::Net::LDAP::InstrumentationService.new({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought I have here, what if we added this otel instrumentation as a decorator to the injected service?

@scbjans scbjans marked this pull request as ready for review August 18, 2025 12:12
@scbjans scbjans requested a review from arielvalentin August 27, 2025 09:00
Copy link
Contributor

@kaylareopelle kaylareopelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @scbjans, thanks for opening this PR.

I'm not very familiar with LDAP, so I'll take another pass once I learn a bit more. For now, I left a few comments.

In addition to those comments, would you mind updating the CODEOWNERS file to list you as the owner of this gem? PR assignment for CODEOWNERS is currently broken, but I'm hoping to get it fixed soon.

Comment on lines 6 to 8
gem 'opentelemetry-api'
gem 'opentelemetry-common'
gem 'opentelemetry-instrumentation-base'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be able to leave these out, since they're dependencies of the other gems.

Suggested change
gem 'opentelemetry-api'
gem 'opentelemetry-common'
gem 'opentelemetry-instrumentation-base'

tracer.in_span(
event,
attributes: attributes.compact,
kind: :client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points. I was also wondering about SemConv as I'm reading through this. I asked the #opentelemetry-semantic-conventions channel on CNCF Slack for advice.


def instrument(event, payload)
attributes = {
'ldap.auth' => auth.except(:password).to_json,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need auth given we removed it from db etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why it was removed from db, but I think it's valuable to see which user connects to ldap; e.g. to troubleshoot permissions

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it was in relation to the range of different auth methods/mechanism that could be used ie certificates vs user credentials.

The name Auth doesn't feel scoped enough as it could mean many things. In ldap v3 SASL is supported which opens up additional techniques.

@scbjans
Copy link
Contributor Author

scbjans commented Sep 17, 2025

@kaylareopelle thanks for your feedback; I updated it in 7264acb

@thompson-tomo thank you as well for your feedback, I believe I addressed it in 44a1d54
However, tests are failing, because the new opentelemetry-semantic_conventions is missing some requires of the SemConv module. I'll have to tidy up my fixes for that and submit a PR

def instrument(event, payload)
attributes = {
'ldap.auth' => auth.except(:password).to_json,
'ldap.base' => base,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this become ldap.tree.base or similar that way we have potential of adding other properties for the tree.

attributes = {
'ldap.auth' => auth.except(:password).to_json,
'ldap.base' => base,
'ldap.encryption' => encryption.to_json,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure of the benefits of this given it could be a hash of properties.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants